Typeclassopedia的 Monad Transformers 部分解释了:
不幸的是,monad并不像applicative functor那样好用(如果你不需要Monad提供的全部功能,那么使用Applicative的另一个原因)
查看>>=
和<*>
的类型,上述声明对我来说并不清楚。
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
(>>=) :: Monad m => m a -> (a -> m b) -> m b
请解释“monad不像compative functors那样好。”
我读了这篇answer,但你能举个例子来帮助我理解吗?
答案 0 :(得分:39)
有几种概念,类型* -> *
可以&#34;组成&#34;。更重要的是你可以按顺序编写#34;
newtype Compose f g x = Compose { getCompose :: f (g x) }
在这里,您可以看到Compose
有类似(* -> *) -> (* -> *) -> (* -> *)
的内容,就像任何好玩家组合一样。
所以问题是:是否有遵守法律的例子?
instance (Applicative f, Applicative g) => Applicative (Compose f g)
instance (Monad f, Monad g) => Monad (Compose f g)
关于为什么monad不能和应用程序一起构成的简短答案是,虽然第一个实例可以写,但第二个实例不能。试试吧!
我们可以使用Functor
instance (Functor f, Functor g) => Functor (Compose f g) where
fmap f (Compose fgx) = Compose (fmap (fmap f) fgx)
我们在此处看到,因为我们可以fmap
fmap
- 编辑f
我们可以将它传递到图层f
和g
,就像我们需要的那样。使用pure
instance (Applicative f, Applicative g) => Applicative (Compose f g) where
pure a = Compose (pure (pure a))
虽然(<*>)
看起来很棘手,但如果仔细观察,我们会使用与fmap
和pure
相同的技巧。
Compose fgf <*> Compose fgx = Compose ((<*>) <$> fgf <*> fgx)
在所有情况下,我们都可以推动我们需要的运营商&#34;通过&#34;正如我们希望的那样,f
和g
的层次。
但现在让我们来看看Monad
。我不会尝试通过Monad
定义(>>=)
,而是通过join
工作。要实施Monad
,我们需要实施
join :: Compose f g (Compose f g x) -> Compose f g x
使用
join_f :: f (f x) -> f x -- and
join_g :: g (g x) -> g x
或者,如果我们剥离newtype
噪音,我们需要
join :: f (g (f (g x))) -> f (g x)
此时可能很清楚问题是什么 - 我们只知道如何加入f
或g
的连续层,但在这里我们看到他们交织在一起。你会发现我们需要一个交换性属性
class Commute f g where
commute :: g (f x) -> f (g x)
现在我们可以实现
instance (Monad f, Monad g, Commute f g) => Monad (Compose f g)
将(newtype
不可知)join
定义为
join :: f (g (f (g x))) -> f (g x)
join fgfgx = fgx where
ffggx :: f (f (g (g x)))
ffggx = fmap commute fgfgx
fggx :: f (g (g x))
fggx = join_f ffggx
fgx :: f (g x)
fgx = fmap join_g fggx
那么这一切的结果是什么? Applicative
s 总是 Compose
,但Monad
只有Compose
Commute
{/ 1}}。
我们什么时候可以commute
层?以下是一些例子
instance Commute ((->) x) ((->) y) where
commute = flip
instance Commute ((,) x) ((,) y) where
commute (y, (x, a)) = (x, (y, a))
instance Commute ((->) x) ((,) y) where
commute (y, xa) = \x -> (y, xa x)
-- instance Commute ((,) x) ((->) y) does not exist; try to write yourself!
--
-- OR:
-- It turns out that you need to somehow "travel back in time" to make it
-- work...
--
-- instance Commute ((,) x) ((->) y) where
-- commute yxa = ( ..., \y -> let (x, a) = yxa y in a )