3

Prelude 没有像这样定义列表单子有什么原因吗?(注意 的非标准实现>>。)

instance  Monad []  where
    m >>= k          = concat (map k m)
    m >> k           = k                 -- a.k.a. flip const
    return x         = [x]
    fail s           = []

我尝试根据单子定律检查这一点,但他们没有提到>>. Monad类定义是这样的:

m >> k = m >>= \_ -> k

在这种[]情况下,这将转化为:

concat (map (\_ -> k) m)

这当然不等同于——它们flip const产生明显不同的结果,例如,[1..5] >> return 1。但是我不清楚这个默认定义是实例必须遵守的法律,还是只是满足实现也会满足Monad的其他法律的默认实现。flip const

直观地说,鉴于 list monad 的意图>>(“非确定性计算”),由于修剪分支保证等于一个,它的替代定义似乎同样好,如果不是更好的话。或者另一种说法是,如果我们处理的是集合而不是列表,那么两个候选定义将是等价的。但是我是否在这里遗漏了一些使flip const列表的定义错误的微妙之处?

编辑:ehird 的回答与上述内容有一个非常明显的缺陷,即它得到了错误的预期结果[] >> k,应该是[],不是k。不过,我认为这个问题可以修改为这个定义:

[] >> k = []
_ >> k = k
4

3 回答 3

14

a >> b必须始终等价于a >>= const b; 它仅在Monad类中,以便您可以定义更有效(但在语义上等效)的版本。这就是为什么它不是 monad 法则的一部分:它实际上不是 monad 定义的一部分,只是类型类(如fail)。

不幸的是,我在基本包的文档中找不到明确说明这一点的任何地方,但我认为旧版本可能已(>>)在类型类之外定义Monad

对于它的价值,您的定义(>>)打破了 list monad 用于非确定性计算的用途。由于[]用于表示失败,[] >> m必须始终为[]; 用尽所有可能的分支后,您将无法继续!这也意味着这两个程序:

do { m; ... }
do { _ <- m; ... }

可能在行为上有所不同,因为前者用 脱糖,(>>)而后者用(>>=). (参见Haskell 2010 年报告。)

于 2012-05-08T00:15:44.557 回答
4

因为ma >> mbjustma >>= \_ -> mb. _ 如果仅在列表的情况下>>定义,则如果flip const是列表,则根本ma >> mb不会运行计算,这将非常混乱。ma ma

至于为什么它没有被定义为flip const general,好吧,它的当前语义>>使它能够在您不关心其结果的情况下对效果进行排序,这通常很有用。

于 2012-05-08T00:17:12.350 回答
2

您的定义>>违反了Monad结合律:

newtype B a = B { unB :: [a] }

instance Monad B where
  m >>= f = B . concatMap (unB.f) $ unB m
  (>>) = flip const
  return a = B [a]

case1 = B [1,2,3] >>= (\_ -> B [4,5,6] >> return 1)
case2 = (B [1,2,3] >>= \_ -> B [4,5,6]) >> return 1

main = do
  print $ unB case1
  print $ unB case2

上述两种情况的关联性不同,但产生的结果不同。

于 2012-05-08T04:23:23.390 回答