撰写Monads v.Applicative Functors

Kev*_*ith 29 haskell

Typeclassopedia单子变形金刚节介绍:

不幸的是,monad并不像applicative functor那样组合(如果你不需要Monad提供的全部功能,那么使用Applicative的另一个原因)

纵观类型的>>=<*>,上面的语句,我不清楚.

(<*>) :: Applicative f => f (a -> b) -> f a -> f b
(>>=) :: Monad m => m a -> (a -> m b) -> m b
Run Code Online (Sandbox Code Playgroud)

请解释一下"monad不像compative functors那样好."

我看了这个答案,但你能举个例子来帮我理解吗?

J. *_*son 40

有几种类型* -> *可以"组合"的概念.更重要的是你可以"按顺序"组合它们.

newtype Compose f g x = Compose { getCompose :: f (g x) }
Run Code Online (Sandbox Code Playgroud)

在这里,你可以看到它Compose有点(* -> *) -> (* -> *) -> (* -> *)像任何好的构造函数应该.

所以问题是:是否有遵守法律的例子如下?

instance (Applicative f, Applicative g) => Applicative (Compose f g)
instance (Monad f,       Monad g)       => Monad       (Compose f g)
Run Code Online (Sandbox Code Playgroud)

关于为什么monad不能和应用程序组合的简短答案是,虽然第一个实例可以写入,但第二个实例不能.我们试试吧!


我们可以热身了 Functor

instance (Functor f,     Functor g)     => Functor     (Compose f g) where
  fmap f (Compose fgx) = Compose (fmap (fmap f) fgx)
Run Code Online (Sandbox Code Playgroud)

在这里,我们看到,由于我们fmapfmap-ed f,我们可以通过它通过层fg像我们需要.玩类似的游戏pure

instance (Applicative f, Applicative g) => Applicative (Compose f g) where
  pure a = Compose (pure (pure a))
Run Code Online (Sandbox Code Playgroud)

同时(<*>)出现棘手的,如果你仔细看,这是我们既使用了完全相同的伎俩fmappure.

  Compose fgf <*> Compose fgx = Compose ((<*>) <$> fgf <*> fgx)
Run Code Online (Sandbox Code Playgroud)

在任何情况下,我们可以"通过"层层推进,我们需要运营商fg完全一样,我们可能希望.

但现在让我们来看看Monad.而不是试图定义Monad通过(>>=),我将改为通过工作join.要实现Monad我们需要实现

join :: Compose f g (Compose f g x) -> Compose f g x
Run Code Online (Sandbox Code Playgroud)

运用

join_f :: f (f x) -> f x  -- and
join_g :: g (g x) -> g x
Run Code Online (Sandbox Code Playgroud)

或者,如果我们剥离newtype噪音,我们需要

join :: f (g (f (g x))) -> f (g x)
Run Code Online (Sandbox Code Playgroud)

在这一点上可能很清楚问题是什么 - 我们只知道如何连接s或s的连续层,但在这里我们看到它们交织在一起.你会发现我们需要一个交换属性fg

class Commute f g where
  commute :: g (f x) -> f (g x)
Run Code Online (Sandbox Code Playgroud)

现在我们可以实施了

instance (Monad f, Monad g, Commute f g) => Monad (Compose f g)
Run Code Online (Sandbox Code Playgroud)

与(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
Run Code Online (Sandbox Code Playgroud)

那么这一切的结果是什么?Applicatives ^ 始终 Compose,但MonadŞ 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 )
Run Code Online (Sandbox Code Playgroud)

  • 值得一提的是,因为`Traversable`类类提供了`sequence`函数,它具有这种类型的签名:`sequence :: Monad m => t(ma) - > m(ta)`,你可以定义` NestedMonads`为`类型NestedMonads mt =(Monad m,Traversable t,Monad t)`然后`bibind :: NestedMonads mt => m(ta) - >(a - > m(tb)) - > m(tb) `.[code](http://lpaste.net/130180). (7认同)