为什么monad没有在组合下关闭?

问题描述 投票:11回答:2

当我从Haskell Book学习Composing Types章节时,我被赋予了编写以下类型的Functor和Applicative实例的任务。

newtype Compose f g a = Compose { getCompose :: f (g a) }

我写了以下定义

仿函数:

fmap f (Compose fga) = Compose $ (fmap . fmap) f fga

应用:

(Compose f) <*> (Compose a) = Compose $ (<*>) <$> f <*> a

我了解到,组成两个Functor或Applicative分别给出了Functor和Applicative。

作者还解释说,不可能以同样的方式组成两个Monads。所以我们使用Monad变形金刚。我只是不想阅读Monad变形金刚,除非我清楚为什么Monads不写作。

到目前为止,我试图写这样的bind函数:

单子:

(>>=) :: Compose f g a -> (a -> Compose f g b) -> Compose f g b
(Compose fga) >>= h = (fmap.fmap) h fga

当然从GHC得到了这个错误

预期类型:撰写f g b

实际类型:f(g(撰写f g b))

如果我能以某种方式去除最外面的f g,那么这个构图给了我们一个monad吧? (我仍然无法弄清楚如何剥离它)

我尝试从其他Stack Overflow问题中读取答案,例如this,但所有答案都更理论化或者数学。我还没有意识到为什么莫纳德不会写作。没有使用数学,有人可以解释我吗?

haskell monads composition monad-transformers
2个回答
18
投票

我认为通过查看join运算符最容易理解:

join :: Monad m => m (m a) -> m a

join是用于定义>>=Monad的替代品,并且更容易推理。 (但现在你要做一个练习:展示如何从>>=实现join,以及如何从join实现>>=!)

让我们尝试为join做一个Composed f g操作,看看出了什么问题。我们的输入本质上是f (g (f (g a)))类型的值,我们想要生成f (g a)类型的值。我们也知道我们有joinf单独的g,所以如果我们可以得到f (f (g (g a)))类型的值,那么我们可以用fmap join . join命中它来获得我们想要的f (g a)

现在,f (f (g (g a)))f (g (f (g a)))不远。我们真正需要的是这样的函数:distribute :: g (f a) -> f (g a)。然后我们可以像这样实现join

join = Compose . fmap join . join . fmap (distribute . fmap getCompose) . getCompose

注意:有些法律我们希望distribute满足,以确保我们到达的join是合法的。


好吧,这表明如果我们有一个分配法distribute :: g (f a) -> f (g a)我们如何组成两个monad。现在,每一对monad都有一个分配定律。也许我们只需要认真考虑如何写一个?

不幸的是,有一对没有分配法的monad。因此,我们可以通过生成两个绝对没有办法将g (f a)变成f (g a)的单子来回答你原来的问题。这两个单子将见证单子一般不构成的事实。

我声称g = IOf = Maybe没有分配法

-- Impossible!
distribute :: IO (Maybe a) -> Maybe (IO a)

让我们思考为什么这样的事情应该是不可能的。此函数的输入是一个IO动作,它进入现实世界并最终生成NothingJust x。这个函数的输出是Nothing,或者Just是一个IO动作,在运行时最终产生x。要制作Maybe (IO a),我们将不得不窥探未来并预测IO (Maybe a)将要做的事情!


综上所述:

  • 如果存在分配法g (f a) -> f (g a),Monads只能撰写。
  • 有些monad没有这样的分配法。
  • 有些单子可以互相构成,但并不是每一对单子都可以构成。

1
投票
© www.soinside.com 2019 - 2024. All rights reserved.