2012-01-16 16 views
5

Tak więc próbuję utworzyć typ dla krotek o zmiennej długości, zasadniczo jako ładniejszej wersji Either a (Either (a,b) (Either (a,b,c) ...)) i Either (Either (Either ... (x,y,z)) (y,z)) z.GHC: brak określenia parametru typu fantomowego

{-# LANGUAGE TypeOperators, TypeFamilies, MultiParamTypeClasses, FlexibleInstances #-} 
module Temp where 

-- type level addition 
data Unit 
data Succ n 

class Summable n m where 
    type Sum n m :: * 

instance Summable Unit m where 
    type Sum Unit m = Succ m 

instance Summable n m => Summable (Succ n) m where 
    type Sum (Succ n) m = Succ (Sum n m) 

-- variable length tuple, left-to-right 
data a :+ b = a :+ Maybe b 
infixr 5 :+ 

class Prependable t r s where 
    type Prepend t r s :: * 
    prepend :: r -> Maybe s -> Prepend t r s 

instance Prependable Unit x y where 
    type Prepend Unit x y = x :+ y 
    prepend = (:+) 

instance Prependable n x y => Prependable (Succ n) (w :+ x) y where 
    type Prepend (Succ n) (w :+ x) y = w :+ Prepend n x y 
    prepend (w :+ Nothing) _ = w :+ Nothing 
    prepend (w :+ Just x) y = w :+ Just (prepend x y) 

-- variable length tuple, right-to-left 
data a :- b = Maybe a :- b 
infixl 5 :- 

class Appendable t r s where 
    type Append t r s :: * 
    append :: Maybe r -> s -> Append t r s 

instance Appendable Unit x y where 
    type Append Unit x y = x :- y 
    append = (:-) 

instance Appendable n x y => Appendable (Succ n) x (y :- z) where 
    type Append (Succ n) x (y :- z) = Append n x y :- z 
    append _ (Nothing :- z) = Nothing :- z 
    append x (Just y :- z) = Just (append x y) :- z 

Jednak kompilator wydaje stanie wywnioskować parametr typu Upiór prepend lub append w rekurencyjnych przypadkach:

Temp.hs:32:40: 
    Could not deduce (Prepend t1 x y ~ Prepend n x y) 
    from the context (Prependable n x y) 
     bound by the instance declaration at Temp.hs:29:10-61 
    NB: `Prepend' is a type function, and may not be injective 
    In the return type of a call of `prepend' 
    In the first argument of `Just', namely `(prepend x y)' 
    In the second argument of `(:+)', namely `Just (prepend x y)' 

Temp.hs:49:34: 
    Could not deduce (Append t0 x y ~ Append n x y) 
    from the context (Appendable n x y) 
     bound by the instance declaration at Temp.hs:46:10-59 
    NB: `Append' is a type function, and may not be injective 
    In the return type of a call of `append' 
    In the first argument of `Just', namely `(append x y)' 
    In the first argument of `(:-)', namely `Just (append x y)' 

Czy mogę coś zrobić, aby pomóc kompilator zrobić to wnioskowanie?

Odpowiedz

7

Ważną częścią komunikatu o błędzie jest tutaj:

NB: `Prepend' is a type function, and may not be injective 

Co to oznacza? Oznacza to, że może być ich wiele instance Prependable w taki sposób, że ich type Prepend ... = a, tak, że jeśli wnioskujesz o pewnej wartości Prepend na a, nie musisz wiedzieć, do której instancji należy.

Problem ten można rozwiązać za pomocą data types in type families, które mają tę zaletę, że nie radzą sobie z funkcjami typu, które są suriekcją ale może być injective i zamiast z typu „relacji”, które są bijective (Więc każdy Typ Prepend może należeć tylko do jednej rodziny typów, a każda rodzina typów ma odrębny typ Prepend).

(Jeśli chcesz mi pokazać rozwiązanie z typów danych w rodzinach typu, zostaw komentarz! Zasadniczo wystarczy użyć data Prepend zamiast type Prepend)

+0

Ach, dzięki za dokuczanie na znaczenie tego komunikatu o błędzie dla mnie. – rampion

+1

Zauważ, że oryginalny kod * zawiera * używa rodzin typów (w szczególności, typów synonimów); zamiast tego należy użyć rodzin danych. – ehird

+0

@Hird, zawsze uważałem, że "aliasy typów w klasach typów" nadal nie należą do rozszerzenia rodzin typów, ale TIL. – dflemstr

1

Rozwiązanie wymyśliłem było dodanie obojętne argumentu związać prepend i append do parametru phantom:

-- as above, except... 

unsucc :: Succ n -> n 
unsucc _ = undefined 

class Prependable t r s where 
    type Prepend t r s :: * 
    prepend :: t -> r -> Maybe s -> Prepend t r s 

instance Prependable Unit x y where 
    type Prepend Unit x y = x :+ y 
    prepend _ = (:+) 

instance Prependable n x y => Prependable (Succ n) (w :+ x) y where 
    type Prepend (Succ n) (w :+ x) y = w :+ Prepend n x y 
    prepend _ (w :+ Nothing) _ = w :+ Nothing 
    prepend t (w :+ Just x) y = w :+ Just (prepend (unsucc t) x y) 

class Appendable t r s where 
    type Append t r s :: * 
    append :: t -> Maybe r -> s -> Append t r s 

instance Appendable Unit x y where 
    type Append Unit x y = x :- y 
    append _ = (:-) 

instance Appendable n x y => Appendable (Succ n) x (y :- z) where 
    type Append (Succ n) x (y :- z) = Append n x y :- z 
    append _ _ (Nothing :- z) = Nothing :- z 
    append t x (Just y :- z) = Just (append (unsucc t) x y) :- z 
Powiązane problemy