2015-09-17 8 views
7

Ładuje bez błędu:Na deklaracji instancji funktora

data Const c a = Const c 

instance Functor (Const c) where 
    fmap _ (Const v) = Const v 

... ale to

data Const' c a = Const' c 

instance Functor (Const' c) where 
    fmap _ cv = cv 

... nie powiedzie się z:

Couldn't match type `a' with `b' 
     `a' is a rigid type variable bound by 
      the type signature for fmap :: (a -> b) -> Const' c a -> Const' c b 
      at test.hs:4:5 
     `b' is a rigid type variable bound by 
      the type signature for fmap :: (a -> b) -> Const' c a -> Const' c b 
      at test.hs:4:5 
    Expected type: Const' c b 
     Actual type: Const' c a 
    In the expression: cv 
    In an equation for `fmap': fmap _ cv = cv 
    In the instance declaration for `Functor (Const' c)' 

nie rozumiem błąd. Dlaczego kompilator nie może wywnioskować, że typem cv jest ? Cóż innego mogłoby być, biorąc pod uwagę resztę deklaracji i definicję fmap?

+3

Na szczęście startowanie konstruktora 'Const' i jego ponowne włączenie jest bezpłatne, ponieważ' Const' jest newtype. Sytuacja jest znacznie smutniejsza w przypadku "Albo", gdzie pamięć może wymagać przydzielenia, aby zmienić typ, nawet jeśli reprezentacja pozostaje taka sama. – dfeuer

Odpowiedz

9

Jeśli chcesz być w pełni jawne, można napisać

{-# LANGUAGE ScopedTypeVariables, InstanceSigs #-} 

data Const c a = Const c 

instance Functor (Const c) where 
    fmap :: forall a b. (a -> b) -> Const c a -> Const c b 
    fmap _ (Const v :: Const c a) = Const v :: Const c b 

który jest trochę łyk. :-)

forall a b. przynosi a i b w zakresie, aby można było odwoływać się do definicji. Jest to włączone przez ScopedTypeVariables. InstanceSigs pozwala nam na napisanie podpisu fmap w pierwszej kolejności (zwykle musi to być musi być wywnioskowane z klasy, więc nie mamy nigdzie, aby uzyskać nazwy zmiennych typu od).

+2

Czy jesteś pewien, że w tym miejscu należy policzyć tam "c"?Wydaje mi się, że "c" powinno być tym, które jest domyślnie związane przez instancję. – pyon

+2

Masz rację. Ilościowo c w ostatniej chwili, ale jest niepoprawny. Naprawiono – luqui

+0

Jeszcze raz dziękuję. Teraz jest krystalicznie czysty. – kjo

7

cv jest typu Const' c a, więc nie może również być typu Const' c b (chyba że a ~ b). Const v i Const v, jednak mogą być różnych typów.

Innym sposobem patrzenia na to jest fakt, że fmap _ cv = cv jest równoważna fmap _ = id, co byłoby typu Const' c a -> Const' c a, ale fmap _ musi być typu Const' c a -> Const' c b.

+1

Jeszcze innym sposobem patrzenia na to jest to, że składnia haskell sprawia, że ​​parametry typu są niejawne i pobiera je z kontekstu. Pełna forma definicji to 'fmap _ (Const {c a} v) = Const {cb} v' (gdzie' {} 'oznacza aplikację typu), co wyjaśnia, że ​​dwie strony równania są różne. – luqui

+0

@luqui: Czy istnieje sposób napisania pełnej formy definicji akceptowanej przez kompilator? Próbowałem kilka wariantów tego, co pokazałeś, ale kompilator odrzuca je wszystkie. – kjo

+0

@kjo To, co pokazał luqui, nie jest prawdziwą składnią. Nie możesz go używać w programach Haskell. –