haskell - 为什么单子(monad)在组合下不关闭?

标签 haskell monads composition monad-transformers

在我学习的时候Composing Types Haskell Book 的章节中,我被赋予了为以下类型编写 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。
作者还解释了不可能以相同的方式组成两个 Monad。所以我们使用 Monad Transformers。我只是不想阅读 Monad Transformers,除非我清楚为什么 Monads 不作曲。
到目前为止,我试图写 bind像这样的功能:
单子(monad):
(>>=) :: Compose f g a -> (a -> Compose f g b) -> Compose f g b
(Compose fga) >>= h = (fmap.fmap) h fga
当然从 GHC 得到这个错误

Expected type: Compose f g b

Actual type: f (g (Compose f g b))


如果我可以剥离最外面的f g不知何故,这个组合给了我们一个单子(monad),对吧? (我仍然无法弄清楚如何剥离它)
我尝试阅读其他 Stack Overflow 问题的答案,例如 this ,但所有答案都更理论化或数学化。我仍然不明白为什么 Monads 不组成。有人可以在不使用数学的情况下解释我吗?

最佳答案

我认为这通过查看 join 最容易理解。运算符(operator):

join :: Monad m => m (m a) -> m a
join>>= 的替代品用于定义 Monad ,并且更容易推理。 (但现在你要做一个练习:展示如何从 >>= 实现 join,以及如何从 join 实现 >>=!)
让我们尝试制作一个 join Composed f g 的操作看看出了什么问题。我们的输入本质上是 f (g (f (g a))) 类型的值, 我们想要产生一个类型为 f (g a) 的值.我们也知道我们有 join对于 fg单独,所以如果我们能得到一个类型为 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 :: (Monad f, Monad g) => g (f a) -> f (g a),我们如何组成两个 monad。 .现在,每一对单子(monad)都有一个分配律,这可能是真的。也许我们只需要认真思考如何写下来?
不幸的是,有成对的单子(monad)没有分配律。因此,我们可以通过生成两个绝对无法转换 g (f a) 的单子(monad)来回答您最初的问题。进入 f (g a) .这两个 monad 将见证 monad 通常不组成的事实。
我声称 g = IOf = Maybe没有分配规律
-- Impossible!
distribute :: IO (Maybe a) -> Maybe (IO a)
让我们想想为什么这样的事情是不可能的。这个函数的输入是一个 IO Action ,它进入现实世界并最终产生 NothingJust x .此函数的输出是 Nothing , 或 Just一个 IO 操作,在运行时最终会产生 x .生产 Maybe (IO a) ,我们将不得不窥视 future 并预测IO (Maybe a)行动是要做的!

总之:
  • 如果有分配律,Monad 可以组合 g (f a) -> f (g a) . (但请参阅下面的附录)
  • 有些单子(monad)没有这样的分配规律。
  • 一些 monad 可以相互组合,但不是每对 monad 都可以组合。

  • 附录:“如果”,但“仅当”呢?如果 F 的所有三个, G , 和 FG是单子(monad),那么你可以构造一个自然变换δ : ∀X. GFX -> FGX作为 GFη_X : GFX -> GFGX 的组成后跟 η_{GFGX} : GFGX -> FGFGX然后由 μ_X : FGFGX -> FGX .在 Haskelese 中(为了清楚起见,使用显式类型应用程序),那将是
    delta :: forall f g x. (Monad f, Monad g, Monad (Compose f g))
          => g (f x) -> f (g x)
    delta = join' . pure @f . fmap @g (fmap @f (pure @g)) 
      where
        -- join for (f . g), via the `Monad (Compose f g)` instance
        join' :: f (g (f (g x))) -> f (g x)
        join' = getCompose . join @(Compose f g) . fmap Compose . Compose
    
    所以如果作文FG是一个单子(monad),那么你可以得到一个具有正确形状的自然变换,成为一个分配律。但是,为了确保您的分配律满足正确的属性,还有一些额外的限制,上面模糊地提到过。一如既往,the n-Category Cafe has the gory details .

    关于haskell - 为什么单子(monad)在组合下不关闭?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55044292/

    相关文章:

    python - 如何使用 __getattr__ 将方法委托(delegate)给属性?

    haskell - 是否有用于更新嵌套数据结构的 Haskell 习惯用法?

    haskell - 如何在 Happstack 中创建数据库 Monad Stack?

    haskell - 在三便士 Canvas 上绘制图像

    monads - 将 Show 实例添加到 RWH 的 RandomState 示例

    Haskell 解析器排序不起作用

    oop - 对象的组成

    java - 继承与组合的区别

    haskell - Type构造函数接受相同类型的几种数据类型,如何获得同名访问器?

    haskell - 将函数调用移动到 where 子句会破坏带有重叠实例的类型检查器