【问题标题】:Why should fail method exist in the monad type class?为什么monad类型类中应该存在fail方法?
【发布时间】:2015-05-30 11:46:30
【问题描述】:

所以我有这行代码:

[Nothing] >>= \(Just x) -> [x]

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

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

do
  Just x <- [Nothing]
  return x

在我看来,它们应该产生相同的结果,因为应该将 do-blocks 减少为使用 (>>=) 并返回。但事实并非如此,使 do-notation 成为一种特性,而不是一种语法糖。

我知道 monad 类型类中存在 fail 并且我知道当模式匹配在 do-block 中失败时会调用它,但我不明白为什么这是一种与使用正常不同的想要的行为monad 操作。

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

【问题讨论】:

  • 还是语法糖,只是糖比你想象的要甜一点。
  • 如果它真的一个语法糖,那么它如何被减少为使用bind和return?
  • Haskell Report 对此有明确的答案。

标签: haskell pattern-matching monads do-notation


【解决方案1】:

代码如

\(Just x) -> ...

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

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

选择是好是坏可能会引起争议。只是为了展示另一个设计选项,Monad 类可以在没有fail 的情况下设计,以及诸如

之类的块
do ... 
   Just x <- ...
   ...

可能已经被禁止,或者需要一个特殊的 MonadFail Monad 子类。 Erroring out也是一种选择,但我们喜欢写e.g.

catMaybes xs = do Just x <- xs
                  return x
-- or
catMaybes xs = [ x | Just x <- xs ]

从列表中丢弃Nothings。

【讨论】:

  • 还有一种可能性,它甚至可以超越do 表示法:使我们无法证明的所有模式匹配都以_ -&gt; patternMatchFail "Line number" 隐式结束,然后使class PatternMatchFailable pmf where patternMatchFail :: String -&gt; pmf 结束。任何不属于该类的内容都会被捕获为编译器错误。 (注意,是 output 类型,而不是 input 类型,它表示模式匹配是否会失败。)
  • 我不明白为什么它应该有不同的行为。
  • @ChrisDrost 这会破坏引用透明度(就像当前使用 fail 的翻译遗憾地已经对 Monad 的特定实现所做的那样):内联定义会以某种方式改变行号可在程序中观察到。
  • @DanielWagner:有趣的一点,但我不确定您所说的是“参照透明度”:我相信您期望两个具有完全相同定义的函数是完全相同的函数独立于该代码的编写位置,而这种方法并不总是如此,因为隐式插入的代码行。但是这些行目前可以由预处理器显式插入,因此在 Haskell 级别上没有真正的 语义 差异。
  • @ChrisDrost 我同意引用透明性是关于在语义上用变量的定义替换变量。我声称用let foo = do { False &lt;- return True; return () } in do { False &lt;- return True; return () } 替换let foo = do { False &lt;- return True; return () } in foo 语义替换,并在GHC 中产生不同的结果。因为你不能安全地进行这种替换,所以你扔掉了一个非常强大的推理工具和一个非常实用和常用的重构工具。
猜你喜欢
  • 2015-03-20
  • 1970-01-01
  • 2013-08-12
  • 2021-11-19
  • 1970-01-01
  • 2016-06-27
  • 2019-02-18
  • 2016-07-17
  • 2020-12-31
相关资源
最近更新 更多