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).
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. –