Haskell 哈斯克尔:为什么当用作单子时,Maybe和任一类型的行为都不同?

Haskell 哈斯克尔:为什么当用作单子时,Maybe和任一类型的行为都不同?,haskell,error-handling,monads,Haskell,Error Handling,Monads,我正试图了解Haskell中的错误处理。我找到了“”这篇文章,但我不明白为什么可能和两者的行为有所不同 例如: import Control.Monad.Error myDiv :: (Monad m) => Float -> Float -> m Float myDiv x 0 = fail "My divison by zero" myDiv x y = return (x / y) testMyDiv1 :: Float -> Float -

我正试图了解Haskell中的错误处理。我找到了“”这篇文章,但我不明白为什么可能和两者的行为有所不同

例如:

import Control.Monad.Error

myDiv :: (Monad m) => Float -> Float -> m Float
myDiv x 0 = fail "My divison by zero"
myDiv x y = return (x / y)

testMyDiv1 :: Float -> Float -> String
testMyDiv1 x y =
    case myDiv x y of
        Left e  -> e
        Right r -> show r

testMyDiv2 :: Float -> Float -> String
testMyDiv2 x y =
    case myDiv x y of
        Nothing -> "An error"
        Just r  -> show r
调用
testMyDiv2 1 0
会产生
一个“错误”
,但调用
testMyDiv1 1 0
会产生以下结果:

"*** Exception: My divison by zero
(注意缺少结束引号,表示这不是字符串,而是异常)


给你什么?

我猜你使用的是
monads-fd

$ ghci t.hs -hide-package mtl
*Main Data.List> testMyDiv1 1 0
"*** Exception: My divison by zero
*Main Data.List> :i Either
...
instance Monad (Either e) -- Defined in Control.Monad.Trans.Error
...
查看包,即
monads fd
获取实例的地方,我们看到:

instance Monad (Either e) where
    return        = Right
    Left  l >>= _ = Left l
    Right r >>= k = k r
因此,没有失败的定义。一般来说,
fail
是不被鼓励的,因为它并不总是保证在monad中完全失败(许多人希望看到
fail
从monad类中删除)

编辑:我应该补充一点,不清楚
fail
是否打算作为默认的
error
调用。对haskell cafe或维护者的ping可能是值得的


EDIT2:mtl的
instance已被删除,此操作包括删除
fail=Left
的定义,并讨论做出该决定的原因。据推测,他们希望人们在Monad失败时更多地使用error,从而将
fail
保留为更灾难性的情况,例如错误模式匹配(例如:
Just x*m Nothing
)。

简而言之,Haskell中的Monad类将
fail
操作添加到Monad的原始数学思想中,这使得如何将这两种类型转换为(Haskell)
Monad
,有些争议,因为有很多方法可以实现

有几个实现可以做不同的事情。我知道的3种基本方法是:

  • fail=Left
    。这似乎是大多数人所期望的,但实际上不能在严格的Haskell 98中实现。实例必须声明为
    instance Monad(任一字符串)
    ,这在H98下是不合法的,因为它提到了
    or
    s参数之一的特定类型(在GHC中,FlexibleInstances扩展将导致编译器接受它)
  • 忽略
    fail
    ,使用只调用
    error
    的默认实现。这就是您的示例中发生的情况。这个版本的优点是兼容H98,但缺点是让用户感到惊讶(在运行时会有惊喜)
  • fail
    实现调用其他类将字符串转换为任何类型。这是在MTL的
    Control.Monad.Error
    模块中完成的,该模块声明
    实例错误e=>Monad(或e)
    。在这个实现中,
    fail msg=Left(strMsg msg)
    。这一个同样是合法的H98,并且偶尔会让用户感到惊讶,因为它引入了另一个类型类。不过,与上一个示例相反,令人惊讶的是编译时

我使用mtl-1.1.0.2为
testMyDiv1 1 0
获取
“我的零除法”
。您使用的是哪个
Control.Monad.Error
?正如sepp2k所说,它取决于
实例Monad
的声明。理想情况下,您应该使用最新的
实例Monad(字符串)
,或者使用
mtl
Monad-{fd,tf}
中的一个“您使用的是旧软件包'base'版本3.x。未来的GHC版本将不支持基本版本3.x。您应该更新代码以使用新的基本版本4.x。“错误。那么也许我的ghc安装已经过时了?阴谋集团的更新不适用任何东西。我正在考虑破坏我的安装并重新开始。
base
不会被cabal安装更新,因为GHC使用base,您必须同时更新两者。我建议您使用GHC 6.12.x或等待GHC 7.0。如果monad类依赖于我,它应该仅限于(>>=)。您对
return
?;-)有何反对意见@kmm实际上,数学定义不包含
fail
,但它是为了提供简洁的“do”语法而添加的,因此它是一个必要的缺点。@sepp2k参见真实世界haskell中do块的去糖化一章<如果模式匹配在do绑定中失败,则调用code>fail
。@TomMD:实际上不是。带有lambda的版本将抛出异常,
do
将使用
fail
,从而返回
Left
(当然,除非您使用的是未定义fail的错误实例版本,在这种情况下,它是相同的,但只是巧合)。关于上述ML讨论的另一个建议是
fail-msg=Left(error-msg)
,但由于许多原因,这很难看。