为什么 monad 类型类中应该存在fail方法?

Brr*_*rch 5 monads haskell pattern-matching do-notation

所以我有这行代码:

[Nothing] >>= \(Just x) -> [x]
Run Code Online (Sandbox Code Playgroud)

这当然会产生异常,因为该模式与 Nothing 不匹配。

另一方面,这段代码给出了不同的结果,[]:

do
  Just x <- [Nothing]
  return x
Run Code Online (Sandbox Code Playgroud)

在我看来,它们应该产生相同的结果,因为 do 块应该被脱糖为使用 (>>=) 并返回。但事实并非如此,这使得 do 符号成为一种功能而不是语法糖。

我知道 monad 类型类中存在失败,并且我知道当 do 块中的模式匹配失败时会调用它,但我无法理解为什么它是一种需要的行为,应该与使用正常的 monad 操作不同。

所以我的问题是 - 为什么要存在失败方法?

chi*_*chi 2

代码如

\(Just x) -> ...
Run Code Online (Sandbox Code Playgroud)

表示一个函数。只有一种方法可以使用这样的值:将其应用于某个参数。当所述参数与模式不匹配(例如 is Nothing)时,应用程序是不可能的,唯一的通用选项是引发运行时错误/异常。

相反,当在do-block 中时,我们有一个类型类:一个 monad。从理论上讲,此类可以扩展为此类情况提供行为。事实上,Haskell 的设计者决定fail为这种情况添加一个方法。

这个选择是好还是坏可能会引起争议。只是为了提供另一种设计选项,该类Monad的设计可以没有fail, 和诸如

do ... 
   Just x <- ...
   ...
Run Code Online (Sandbox Code Playgroud)

可能会被禁止,或者需要MonadFailMonad 的特殊子类。错误也是一种选择,但我们喜欢写例如

catMaybes xs = do Just x <- xs
                  return x
-- or
catMaybes xs = [ x | Just x <- xs ]
Run Code Online (Sandbox Code Playgroud)

Nothing从列表中丢弃s。