2015-04-23 24 views
9

Próbuję zdefiniować typ dla list o stałej długości w Haskell. Kiedy używam standardowego sposobu kodowania liczb naturalnych jako typów w trybie jednoargumentowym, wszystko działa poprawnie. Jednak kiedy próbuję budować wszystko na literałach typu GHC, napotykam mnóstwo problemów.Listy o stałej długości i typie literowym

Mój pierwszy strzał w żądanym typu listy był

data List (n :: Nat) a where 
    Nil :: List 0 a 
    (:>) :: a -> List n a -> List (n+1) a 

które niestety nie pozwalają na pisanie funkcji zip z rodzaju

zip :: List n a -> List n b -> List n (a,b) 

mogę rozwiązać ten problem poprzez odjęcie 1 od typ zmienna n w typie (:>):

data List (n :: Nat) a where 
    Nil :: List 0 a 
    (:>) :: a -> List (n-1) a -> List n a -- subtracted 1 from both n's 

Następnie próbowałem zdefiniować funkcję append:

append :: List n1 a -> List n2 a -> List (n1 + n2) a 
append Nil  ys = ys 
append (x :> xs) ys = x :> (append xs ys) 

Niestety, GHC mówi mi

Couldn't match type ‘(n1 + n2) - 1’ with ‘(n1 - 1) + n2’ 

Dodanie ograniczenia ((n1 + n2) - 1) ~ ((n1 - 1) + n2) do podpisu nie rozwiązuje problemów, ponieważ GHC narzeka

Could not deduce ((((n1 - 1) - 1) + n2) ~ (((n1 + n2) - 1) - 1)) 
from the context (((n1 + n2) - 1) ~ ((n1 - 1) + n2)) 

Teraz, oczywiście, złapałem jakąś nieskończoną pętlę.

Chciałbym więc wiedzieć, czy możliwe jest zdefiniowanie typu dla list o stałej długości przy użyciu literałów typu w ogóle. Czy mógłbym nawet nadzorować bibliotekę właśnie w tym celu? Zasadniczo jedynym wymaganiem jest to, że chcę napisać coś w stylu List 3 a zamiast .

+0

można znaleźć dyskusję na temat zagadnień związanych długości wektora w papierze Hasochism na poziomie typu-: https://personal.cis.strath.ac.uk/conor.mcbride/pub/hasochism. pdf – chi

+1

"Hasochizm" brzmi naprawdę kusząco. Dam jednak papierowi szansę. Dziękuję Ci. –

+0

Najprawdopodobniej łatwiejsze jest otworzenie nowego typu na zwykłej liście z dołączonym 'Nat', podobnie jak robi to' Linear.V'. Możesz zdefiniować kilka prymitywów w jednym module i ukryć konstruktor, aby wszystko było bezpieczne. – cchalmers

Odpowiedz

19

To nie jest odpowiedź.

Korzystanie https://hackage.haskell.org/package/ghc-typelits-natnormalise-0.2 ten

{-# LANGUAGE GADTs #-} 
{-# LANGUAGE TypeOperators #-} 
{-# LANGUAGE DataKinds #-} 
{-# LANGUAGE KindSignatures #-} 
{-# OPTIONS_GHC -fplugin GHC.TypeLits.Normalise #-} 

import GHC.TypeLits 

data List (n :: Nat) a where 
    Nil :: List 0 a 
    (:>) :: a -> List n a -> List (n+1) a 

append :: List n1 a -> List n2 a -> List (n1 + n2) a 
append Nil  ys = ys 
append (x :> xs) ys = x :> (append xs ys) 

... kompiluje, więc oczywiście jest to poprawne.

???

+2

Doskonałe znalezisko. –

+0

W ogóle nie wiedziałem o wtyczkach do sprawdzania typu, ale to działa naprawdę ładnie. Dziękuję Ci. –

6

Literały z liczbą poziomów typu nie mają jeszcze struktury, na której możemy wykonać indukcję, a wbudowany program do rozwiązywania ograniczeń może wykryć tylko najprostsze przypadki. Obecnie lepiej trzymać się Peano naturals.

Jednak nadal możemy używać literałów jako cukru syntaktycznego.

{-# LANGUAGE 
    UndecidableInstances, 
    DataKinds, TypeOperators, TypeFamilies #-} 

import qualified GHC.TypeLits as Lit 

data Nat = Z | S Nat 

type family Lit n where 
    Lit 0 = Z 
    Lit n = S (Lit (n Lit.- 1)) 

Teraz można napisać List (Lit 3) a zamiast .

+0

Miałem podobny pomysł, ale użycie 'UndecidableInstances' zawsze trochę mnie przeraża. Używając innego synonimu, mogłem nawet dojść do "Listy 3 a". –

+0

Tutaj rodzina typów oczywiście kończy działanie, więc problem z "UndecidableInstances" jest zerowy. Nawet w ogólnym przypadku nie uważam tego za naprawdę przerażające. Jeśli przypadkowo napiszemy rozbieżny kod poziomu, prawie zawsze otrzymamy błąd ograniczenia głębi kontekstu. Bardzo rzadko zdarza się, że możemy uzyskać pętlę GHC i możemy łatwo rozwiązać ten problem za pomocą Ctr-c. –

Powiązane problemy