Monad法律解释

Cas*_*ash 33 monads haskell

对Haskell的温和介绍,有以下monad法则.任何人都可以直观地解释他们的意思吗?

return a >>= k             = k a
m >>= return               = m
xs >>= return . f          = fmap f xs
m >>= (\x -> k x >>= h)    = (m >>= k) >>= h
Run Code Online (Sandbox Code Playgroud)

这是我的尝试解释:

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

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

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

  4. 我对这部法律没有解释.这确实说monad必须"几乎联想".

Joh*_*n L 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
Run Code Online (Sandbox Code Playgroud)

现在法律可以写成:

1) return >=> mf = mf                  -- left identity
2) mf >=> return = mf                  -- right identity
4) (f >=> g) >=> h = f >=> (g >=> h)   -- associativity
Run Code Online (Sandbox Code Playgroud)

1)左侧身份法 - 返回一个值不会改变该值,也不会在monad中执行任何操作.

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

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

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

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

其中很多来自Haskell Wiki.该Typeclassopedia是一个很好的参考了.


C. *_*ann 22

与其他的答案没有分歧,但它可能会不由自主地想起的单子法律的实际描述2套房产.正如约翰所说,你提到的第三条法则略有不同,但其他人如何分开:

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

正如在约翰的回答中,对于monad而言,所谓的Kleisli箭头是具有类型的函数a -> m b.想想returnas id(<=<)as (.),monad法则是这些的翻译:

  1. id . f 相当于 f
  2. f . id 相当于 f
  3. (f . g) . h 相当于 f . (g . h)

monadic效应的序列附加像列表.

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

从这个意义上说,return又是一个身份 - 空行为,类似于一个空的行为列表 - 并且(>=>)是连接.因此,monad法律是这些的翻译:

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

这三个定律描述了一种荒谬的共同模式,不幸的是Haskell完全不能完全表达.如果您感兴趣,Control.Category可以概括"看起来像函数组合的东西",同时Data.Monoid概括后一种情况,其中不涉及类型参数.


ken*_*ytm 12

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

    do                          do
                                  y <- do
      x <- m                             x <- m
      y <- k x          <=>              k x
      h y                         h y
Run Code Online (Sandbox Code Playgroud)

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


Pau*_*son 5

前三个定律说“返回”只包装一个值,不做任何其他事情。所以你可以在不改变语义的情况下消除“返回”调用。

最后一条定律是 bind 的结合律。这意味着您需要采取以下措施:

do
   x <- foo
   bar x
   z <- baz
Run Code Online (Sandbox Code Playgroud)

并把它变成

do
   do
      x <- foo
      bar x
   z <- baz
Run Code Online (Sandbox Code Playgroud)

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

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