2012-12-14 25 views
5

Pracowałem przez książkę great good, ale mam problemy z Applicative Funktorów.Applicative Funktorów i lewo od

W poniższym przykładzie max zostanie zastosowany do zawartości dwóch funktorów Może i zwróci Just 6.

max <$> Just 3 <*> Just 6 

Dlaczego w poniższym przykładzie jest Left "Hello" zwrócony zamiast zawartości funktorów albo: Left "Hello World"?

(++) <$> Left "Hello" <*> Left " World" 
+0

Jest to tradycyjne użycie tego, że Prawo reprezentuje wartość, którą jesteś zainteresowany, natomiast lewe przedstawia porażkę. Prawe (poprawne) wartości mogą być łączone i modyfikowane za pomocą Applicative i Functor, natomiast zła wartość nad wartością złą będzie uporczywie utrzymywać się, więc to jest dobre dla rzeczy takich jak zgłaszanie pierwszego błędu w sposób, w jaki może być prosty kompilator. – AndrewC

Odpowiedz

10

To dlatego parametr typu na przykład Functor (i Applicative itp) jest drugim parametrem typu. W

Either a b 

typ a, a wartości Left nie dotyczy operacji functorial lub aplikacyjnych, ponieważ są one uważane za przypadki awarii lub w inny sposób niedostępne.

instance Functor (Either a) where 
    fmap _ (Left x) = Left x 
    fmap f (Right y) = Right (f y) 

Zastosowanie Right,

(++) <$> Right "Hello" <*> Right " World" 

dostać konkatenacji.

+0

Jeśli chcesz odwrócić zmienne typu, możesz użyć modułu 'Data.EitherR' z pakietu' errors'. Daje to dwie opcje: 'flipE', która odwraca argument' Either' lub 'EitherR', który owija go w nowy typ, który zamienia kolejność zmiennych, nadając symetryczne instancje' Functor' i 'Applicative'. –

6

Aby dodać doskonałą odpowiedź Daniela, istnieje kilka punktów chciałbym zrobić:

Najpierw here's się aplikacyjnych instancję:

instance Applicative (Either e) where 
    pure    = Right 
    Left e <*> _ = Left e 
    Right f <*> r = fmap f r 

widać, że jest to „krótko- circuiting "- gdy tylko trafi na Left, przerywa i zwraca lewy. Możesz to sprawdzić, analizując dokładność analizy słabego człowieka:

ghci> (++) <$> Left "Hello" <*> undefined 
Left "Hello"        -- <<== it's not undefined :) !! 

ghci> (++) <$> Right "Hello" <*> undefined 
*** Exception: Prelude.undefined   -- <<== undefined ... :(

ghci> Left "oops" <*> undefined <*> undefined 
Left "oops"        -- <<== :) 

ghci> Right (++) <*> undefined <*> undefined 
*** Exception: Prelude.undefined   -- <<== :( 

Po drugie, twój przykład jest nieco skomplikowany. Ogólnie rzecz biorąc, typ funkcji i e w nie są powiązane. Oto <*> s Typ:

(<*>) :: Applicative f => f (a -> b) -> f a -> f b 

Jeśli wykonujemy podstawienie f - >>Either e, otrzymujemy:

(<*>) :: Either e (a -> b) -> Either e a -> Either e b 

Chociaż w przykładzie, e i a mecz, w ogóle ich nie będzie , co oznacza, że ​​nie można polimorficznie implementować instancji aplikacji dla Either e, która stosuje tę funkcję do argumentu po lewej stronie.