种类的类型有多种概念* -> *
可能会“组成”。更重要的是你可以“按顺序”组合它们。
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
an fmap
-ed f
我们可以通过层传递它f
and g
就像我们需要的那样。玩类似的游戏pure
instance (Applicative f, Applicative g) => Applicative (Compose f g) where
pure a = Compose (pure (pure a))
而同时(<*>)
看起来很棘手,如果你仔细看的话,这与我们对两者使用的技巧完全相同fmap
and pure
.
Compose fgf <*> Compose fgx = Compose ((<*>) <$> fgf <*> fgx)
在所有情况下,我们都可以推动我们需要的操作符“穿过”各个层f
and g
正如我们所希望的那样。
但现在让我们看一下Monad
。而不是试图定义Monad
via (>>=)
,我将通过以下方式工作join
。实施Monad
我们需要实施
join :: Compose f g (Compose f g x) -> Compose f g x
using
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
s or g
s,但在这里我们看到了它们交织在一起。你会发现我们需要一个交换律财产
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 always Compose
, but Monad
s Compose
仅当它们的层数Commute
.
我们什么时候可以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 )