适当使用Monad`unrupt`与MonadPlus`mzero`

jbe*_*man 20 monads haskell typeclass

这个问题在设计代码中已经出现过几次,特别是库.似乎有一些兴趣,所以我认为它可能会成为一个很好的社区维基.

failMonad中的方法被一些人认为是疣; 这个课程有点武断,不是来自原始范畴理论.但当然在当前状态下,许多Monad类型都有逻辑和有用的fail实例.

MonadPlus类是Monad的一个子类,它提供了mzero一种在monad中逻辑地封装失败概念的方法.

因此,想要编写一些执行某种失败处理fail的monadic代码的库设计人员可以选择让他的代码使用Monad中的方法或将他的代码限制为MonadPlus类,这样他就可以感觉良好的使用mzero,即使他完全不关心幺半群组合mplus操作.

关于这个主题的一些讨论在这个维基页面中有关改进MonadPlus课程的建议.


所以我想我有一个具体的问题:

什么monad实例,如果有的话,有一个自然的fail方法,但不能是MonadPlus的实例,因为它们没有逻辑实现mplus

但我最感兴趣的是关于这个问题的讨论.谢谢!


编辑:最后一个想法发生在我身上.我最近学到了(尽管它在文档中是正确的fail),monadic"do"符号以模式匹配失败的方式被贬低,就像(x:xs) <- return []调用monad一样fail.

似乎语言设计者必须受到一些自动故障处理的前景的强烈影响,这些故障处理内置于haskell的语法中,包含fail在Monad中.

fuz*_*fuz 10

想一想Either.它的monadic实例看起来像这样:

{-# LANGUAGE FlexibleInstances #-}
instance Monad (Either String) where
  (Left x) >>= _   = Left x
  (Right a)  >>= f = f a
  return           = Right
  fail             = Left
Run Code Online (Sandbox Code Playgroud)

(我们需要FlexibleInstances以便允许一个实例Either String)
因此,Maybe如果发生某些事情,它基本上就像一个可选的错误消息.您无法重新创建此项mzero,因为您无法向失败添加错误消息.它有点不同fail.

每个实例都mplus应该满足这两个定律:

mzero `mplus` a -> a
a `mplus` mzero -> a
Run Code Online (Sandbox Code Playgroud)

简单,不是吗?但这些法律mplus特别重要.有了它们,就可MonadPlus以为它编写一个合理的实例:

instance MonadPlus (Either a) where
  mzero = Left undefined
  mplus (Left _) b = b
  mplus a _        = a
Run Code Online (Sandbox Code Playgroud)

这是什么?它代表了一种选择.如果第一次计算成功,则返回.否则,mplus返回第二个计算.请注意它与(>>)不符合法律的不同之处:

Left a   >>    Right b -> Left a
Left a `mplus` Right b -> Right b
Run Code Online (Sandbox Code Playgroud)

(>>)将在第一次计算时停止,而mplus尝试第二次计算.[]也表现得像这样:

[] >> [1..4] -> []
[] `mplus` [1..4] -> [1,2,3,4]
Run Code Online (Sandbox Code Playgroud)

这只是为了讨论方面,MonadPlus特别是与之mplus相反的方面(>>).