【问题标题】:Explanation of Monad laws单子定律的解释
【发布时间】:2011-03-26 21:43:29
【问题描述】:

来自a gentle introduction to Haskell,有以下单子定律。谁能直观地解释一下它们的意思?

return a >>= k             = k a
m >>= return               = m
xs >>= return . f          = fmap f xs
m >>= (\x -> k x >>= h)    = (m >>= k) >>= h

这是我尝试的解释:

  1. 我们希望返回函数包装a,这样它的一元性质就变得微不足道了。当我们将它绑定到函数时,没有单子效果,它应该只是将a 传递给函数。

  2. m 的解包输出被传递给重新包装它的return。一元的性质保持不变。所以和原来的monad是一样的。

  3. 解包后的值被传递给f,然后重新打包。一元的性质保持不变。这是我们将普通函数转换为一元函数时所期望的行为。

  4. 我对这条法律没有任何解释。这确实表明 monad 必须是“几乎是关联的”。

【问题讨论】:

  • 我正在努力理解这些规律,但我觉得一个好的规律教程将通过反例来实现。

标签: haskell monads


【解决方案1】:

do 表示法而言,规则4 意味着我们可以添加一个额外的do 块来对一系列单子操作进行分组。

    do                          do
                                  y <- do
      x <- m                             x <- m
      y <- k x          <=>              k x
      h y                         h y

这允许返回一元值的函数正常工作。

【讨论】:

    【解决方案2】:

    你的描述看起来很不错。通常人们会说三个单子定律,即 1、2 和 4。您的第三个定律稍有不同,我稍后会谈到。

    对于三个单子定律,我发现使用 Kleisli 组合重写它们时更容易直观地理解它们的含义:

    -- defined in Control.Monad
    (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
    mf >=> n = \x -> mf x >>= n
    

    现在法律可以写成:

    1) return >=> mf = mf                  -- left identity
    2) mf >=> return = mf                  -- right identity
    4) (f >=> g) >=> h = f >=> (g >=> h)   -- associativity
    

    1) Left Identity Law - 返回一个值不会改变该值,也不会在 monad 中做任何事情。

    2) 正确身份法则 - 返回一个值不会改变该值,也不会在 monad 中做任何事情。

    4) 关联性 - 单子组合是关联的(我喜欢 KennyTM 对此的回答)

    这两个恒等定律基本上说的是同一件事,但它们都是必要的,因为return 应该在绑定运算符的两边都有恒等行为。

    现在是第三定律。这条定律本质上是说 Functor 实例和 Monad 实例在将函数提升到 monad 时的行为方式相同,并且都没有任何 monadic。如果我没记错的话,就是当一个 monad 遵守其他三个定律而 Functor 实例遵循 functor 定律时,那么这个陈述永远是正确的。

    其中很多来自Haskell WikiTypeclassopedia 也是一个很好的参考。

    【讨论】:

    • 您在 >=> 和 m>=>n 的定义中使用 m 的事实有点令人困惑
    • @Casebash:尽管对类型变量和数据变量使用相同的名称似乎是标准做法,但我同意这令人困惑。希望现在更容易阅读。
    • 这个答案被严重低估了。老实说,我希望它的投票数量与“如何将数组转换为对象 javascript”的数量级相同
    【解决方案3】:

    与其他答案没有分歧,但将单子定律视为实际描述 两个 组属性可能会有所帮助。正如约翰所说,您提到的第三条定律略有不同,但以下是其他定律的拆分方式:

    绑定到 monad 的函数就像常规函数一样。

    正如约翰的回答,所谓的单子的 Kleisli 箭头是一个类型为 a -&gt; m b 的函数。将return 视为id(&lt;=&lt;) 视为(.),单子法则就是这些的翻译:

    1. id . f 等价于 f
    2. f . id 等价于 f
    3. (f . g) . h 等价于 f . (g . h)

    单子效果的序列追加类似列表。

    在大多数情况下,您可以将额外的一元结构视为与一元值相关的一系列额外行为;例如Maybe 是“放弃”Nothing 和“继续前进”Just。结合两个一元动作,然后本质上连接了它们所持有的行为序列。

    在这个意义上,return 又是一个标识——空操作,类似于一个空的行为列表——而(&gt;=&gt;) 是连接。所以,单子定律是这些的翻译:

    1. [] ++ xs 等价于 xs
    2. xs ++ [] 等价于 xs
    3. (xs ++ ys) ++ zs 等价于 xs ++ (ys ++ zs)

    这三个定律描述了一个可笑的常见模式,不幸的是 Haskell 不能完全概括地表达。如果你有兴趣,Control.Category 给出了“看起来像函数组合的东西”的概括,而Data.Monoid 概括了后一种不涉及类型参数的情况。

    【讨论】:

      【解决方案4】:

      前三个定律说“return”只包装一个值而没有做其他事情。因此,您可以在不改变语义的情况下消除“返回”调用。

      最后一条法则是绑定的结合性。这意味着您采取以下措施:

      do
         x <- foo
         bar x
         z <- baz
      

      把它变成

      do
         do
            x <- foo
            bar x
         z <- baz
      

      不改变意思。当然,您不会完全这样做,但您可能希望将内部“do”子句放在“if”语句中,并希望它在“if”为真时具有相同的含义。

      有时 monad 并不完全遵循这些规律,尤其是在出现某种底部值时。只要其记录在案并且“在道德上是正确的”(即非底部值遵循法律,或者结果以某种其他方式被认为是等效的),就可以了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-03-29
        • 2015-08-07
        • 2018-10-14
        • 2019-04-04
        • 1970-01-01
        • 2017-02-10
        • 2014-10-25
        相关资源
        最近更新 更多