Ну, fmap
это просто (a -> b) -> f a -> f b
, т.е. мы хотим преобразовать результат монадического действия с помощью чистой функции.Это легко написать с помощью нотации:
fmap f m = do
a <- m
return (f a)
или написано "raw":
fmap f m = m >>= \a -> return (f a)
Это доступно как Control.Monad.liftM
.
pure :: a -> f a
- это, конечно, return
.(<*>) :: f (a -> b) -> f a -> f b
немного сложнее.У нас есть действие, возвращающее функцию, и действие, возвращающее ее аргумент, и мы хотим, чтобы действие возвращало ее результат.Снова сделайте запись:
mf <*> mx = do
f <- mf
x <- mx
return (f x)
Или, обессиленный:
mf <*> mx =
mf >>= \f ->
mx >>= \x ->
return (f x)
Тада!Это доступно как Control.Monad.ap
, поэтому мы можем дать полный экземпляр Functor
и Applicative
для любой монады M
следующим образом:
instance Functor M where
fmap = liftM
instance Applicative M where
pure = return
(<*>) = ap
В идеале, мысмог бы указать эти реализации непосредственно в Monad
, чтобы облегчить бремя определения отдельных экземпляров для каждой монады, например, с этим предложением .Если это произойдет, не будет никаких реальных препятствий для того, чтобы сделать Applicative
суперклассом Monad
, так как он гарантирует, что он не сломает существующий код.С другой стороны, это означает, что шаблон, участвующий в определении Functor
и Applicative
экземпляров для данного Monad
, минимален, поэтому легко быть «хорошим гражданином» (и такие экземпляры должны быть определены для любой монады).