2012-02-01 8 views

Odpowiedz

26

Cóż, fmap to tylko (a -> b) -> f a -> f b, tj. Chcemy przekształcić wynik działania monadycznego z czystą funkcją. To proste, aby pisać notacji zrobić:

fmap f m = do 
    a <- m 
    return (f a) 

lub napisane "surowy":

fmap f m = m >>= \a -> return (f a) 

ta jest dostępna jako Control.Monad.liftM. Jest to oczywiście . (<*>) :: f (a -> b) -> f a -> f b jest nieco trudniejsze. Mamy działanie zwracające funkcję i działanie, które zwraca jego argument, i chcemy, aby działanie zwróciło swój wynik. W notacji zrobić ponownie:

mf <*> mx = do 
    f <- mf 
    x <- mx 
    return (f x) 

Albo odcukrzona:

mf <*> mx = 
    mf >>= \f -> 
    mx >>= \x -> 
    return (f x) 

Tada! Jest dostępny jako Control.Monad.ap, więc możemy dać pełną instancję Functor i Applicative dla każdej monady M następująco:

instance Functor M where 
    fmap = liftM 

instance Applicative M where 
    pure = return 
    (<*>) = ap 

Idealnie, bylibyśmy w stanie określić te implementacje bezpośrednio w Monad, odciążenie definiowania oddzielnych instancji dla każdej monady, na przykład z this proposal. Jeśli tak się stanie, nie będzie żadnej prawdziwej przeszkody, aby uczynić Applicative superklasą z Monad, ponieważ zapewni to, że nie złamie żadnego istniejącego kodu. Z drugiej strony oznacza to, że podstawa do definiowania instancji dla danego jest minimalna, więc łatwo być "dobrym obywatelem" (i takie przypadki powinny być zdefiniowane dla każdej monady).

+5

W tej odpowiedzi brakuje jednego ważnego elementu: dowód, że jeśli dana instancja 'Monad'' m' rzeczywiście spełnia prawa Monada, to monadyczne definicje podane dla 'fmap',' pure' i '(<*>)' stosuj się do Funktora i praw aplikacyjnych. Wszystko, co Haskell wymusza, to sprawdzenie typów. –

10

fmap = liftM i (<*>) = ap. Oto linki do kodu źródłowego dla liftM i ap. Przypuszczam, że wiesz, jak wygaść do notacji.