从对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)
这是我的尝试解释:
我们期望返回函数包装,a以便它的monadic性质是微不足道的.当我们将它绑定到一个函数时,没有monadic效果,它应该只传递a给函数.
将解包的输出m传递给return重新包装它.monadic性质保持不变.所以它和原来的monad一样.
将解包的值传递给f然后重新包装.monadic性质保持不变.这是我们将正常函数转换为monadic函数时所期望的行为.
我对这部法律没有解释.这确实说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而言,所谓的Kleisli箭头是具有类型的函数a -> m b.想想returnas id和(<=<)as (.),monad法则是这些的翻译:
id . f 相当于 ff . id 相当于 f(f . g) . h 相当于 f . (g . h)在大多数情况下,您可以将额外的monadic结构视为与monadic值相关的一系列额外行为; 例如Maybe,"放弃" Nothing和"继续" Just.结合两个monadic动作然后基本上连接他们所持有的行为序列.
从这个意义上说,return又是一个身份 - 空行为,类似于一个空的行为列表 - 并且(>=>)是连接.因此,monad法律是这些的翻译:
[] ++ xs 相当于 xsxs ++ [] 相当于 xs(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值的函数正常工作.
前三个定律说“返回”只包装一个值,不做任何其他事情。所以你可以在不改变语义的情况下消除“返回”调用。
最后一条定律是 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 并不完全遵循这些规律,尤其是在出现某种底值时。只要它被记录在案并且“在道德上是正确的”就可以了(即遵循非最低值的法律,或者结果在其他方面被认为是等效的)。