Monad法律解释

时间:2010-08-08 08:29:01

标签: haskell monads

a gentle introduction to Haskell开始,有以下monad法律。任何人都可以直观地解释它们的含义吗?

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

以下是我的尝试解释:

  1. 我们希望return函数包装a,以便它的monadic性质是微不足道的。当我们将它绑定到一个函数时,没有monadic效果,它应该只将a传递给函数。

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

  3. 将展开的值传递给f然后重新包装。 monadic性质保持不变。这是我们将正常函数转换为monadic函数时所期望的行为。

  4. 我对这部法律没有解释。这确实说monad必须“几乎是联想的”。

4 个答案:

答案 0 :(得分:45)

你的描述看起来很不错。一般来说,人们会说三个monad定律,你有1,2和4法律。你的第三定律略有不同,我稍后会谈到它。

对于三个monad定律,我发现直接理解使用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)左侧身份法 - 返回一个值不会改变该值,也不会在monad中执行任何操作。

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

4)相关性 - monadic组合是联想的(我喜欢KennyTM对此的回答)

两个身份法基本上说同样的事情,但它们都是必要的,因为return应该在绑定操作符的两边都有身份行为。

现在是第三定律。这个定律基本上说Functor实例和你的Monad实例在将一个函数提升到monad中时的行为方式相同,并且没有任何monadic。如果我没有弄错的话,那就是当monad遵守其他三个法律并且Functor实例遵守仿函数法则时,这个陈述将永远是真的。

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

答案 1 :(得分:21)

与其他答案没有分歧,但可能有助于将monad法律视为实际描述两个属性集。正如约翰所说,你提到的第三条法则略有不同,但是其他的如何分开:

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

正如在约翰的回答中所说,monad的Kleisli箭头是类型为a -> m b的函数。将return视为id(<=<)视为(.),monad法则是这些翻译:

  1. id . f相当于f
  2. f . id相当于f
  3. (f . g) . h相当于f . (g . h)
  4. 一元效应的序列附加如列表。

    在大多数情况下,您可以将额外的monadic结构视为与monadic值相关的一系列额外行为;例如MaybeNothing“放弃”,Just为“{继续”。结合两个monadic动作然后基本上连接他们所持有的行为序列。

    从这个意义上说,return又是一个身份 - 空行为,类似于一个空行为列表 - 而(>=>)是串联。因此,monad法则是这些的翻译:

    1. [] ++ xs相当于xs
    2. xs ++ []相当于xs
    3. (xs ++ ys) ++ zs相当于xs ++ (ys ++ zs)

    4. 这三个定律描述了一种荒谬的共同模式,但不幸的是,Haskell无法完全表达这种模式。如果你感兴趣,Control.Category给出了“看起来像函数组合的东西”的概括,而Data.Monoid概括了后一种情况,其中没有涉及类型参数。

答案 2 :(得分:12)

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

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

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

答案 3 :(得分:4)

前三条法律规定“回归”只包装一个价值而不做任何其他事情。因此,您可以在不改变语义的情况下消除“返回”调用。

最后的法则是绑定的相关性。这意味着你采取了类似的东西:

do
   x <- foo
   bar x
   z <- baz

并将其转换为

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

没有改变含义。当然你不会这样做,但是你可能想把内部的“do”子句放在“if”语句中,并且当“if”为真时希望它的含义相同。

有时monad并不完全遵循这些定律,特别是当出现某种底值时。只要记录在案并且“在道德上是正确的”(即非最低值遵循法律,或者以其他方式认为结果相同),那就没关系。