撰写Monads v。Applicative Functors

时间:2015-04-05 03:54:17

标签: haskell

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,但你能举个例子来帮助我理解吗?

1 个答案:

答案 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我们可以将它传递到图层fg,就像我们需要的那样。使用pure

播放类似的游戏
instance (Applicative f, Applicative g) => Applicative (Compose f g) where
  pure a = Compose (pure (pure a))

虽然(<*>)看起来很棘手,但如果仔细观察,我们会使用与fmappure相同的技巧。

  Compose fgf <*> Compose fgx = Compose ((<*>) <$> fgf <*> fgx)

在所有情况下,我们都可以推动我们需要的运营商&#34;通过&#34;正如我们希望的那样,fg的层次。

但现在让我们来看看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)

此时可能很清楚问题是什么 - 我们只知道如何加入fg连续层,但在这里我们看到他们交织在一起。你会发现我们需要一个交换性属性

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 )