为什么不是``>>定义为`翻转const`名单单子方法?(Why isn't t

2019-09-17 02:42发布

是有一些原因的前奏没有定义列表单子是这样吗? (注非标准实现>> )。

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实施也将满足。

直观地看,给出的列表单子(“非确定性计算”)的意图 ,它似乎像的另一种定义>>是一样好,如果不是更好多亏了保证是等于下降到只有一个剪枝。 或者说这句话的另一种方式是,如果我们处理 ,而非列表 ,两个候选的定义是等价的。 但是我在这里缺少一些细微之处,使flip const定义错误的列表?

编辑 :ehird的答案抓住一个非常明显的缺陷与上面的,这是它得到了错误的预期结果[] >> k ,应[]k 。 不过,我认为这个问题可以修正,这样的定义:

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

Answer 1:

a >> b必须始终等同于a >>= const b ; 这是只有在Monad类,以便您可以定义一个更有效(但语义上等效 )的版本。 这就是为什么它不是的单子法律的一部分:它不是一个真正的单子的定义的一部分,只有类型类(如fail )。

不幸的是,我不能为基础包在那里,这是明确指出的文档中找到任何地方,但我觉得老版本可能定义(>>)的外Monad类型类。

对于它的价值,你的定义(>>)打破了名单单子的非确定性计算使用。 由于[]用于表示故障, [] >> m必须始终是[] ; 你不能继续你已经用尽了所有可能的分支之后! 这也意味着,这两种方案:

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

可以在行为不同,因为前者desugars (>>)并与后者(>>=) (见哈斯克尔报告2010 )。



Answer 2:

由于ma >> mb恰恰速记ma >>= \_ -> mb 。 如果>>被定义为flip const只是名单的话,那么ma >> mb将无法运行在计算中ma 在所有如果ma是一个列表,这将是非常混乱。

至于为什么它没有被定义为flip const 一般 ,良好,目前的语义>>使得它能够序列,其中你不关心他们的结果,这是非常有用的效果。



Answer 3:

你的定义>>打破了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

这两种情况在它们的结合上面有所不同,但产生不同的结果。



文章来源: Why isn't the list monad method for `>>` defined as `flip const`?