Haskell:为什么Maybe和Either类型在用作Monads时表现不同?

stu*_*ith 13 error-handling monads haskell

我试图了解Haskell中的错误处理.我发现文章" 8种方法来报告Haskell中的错误 ",但我很困惑为什么Maybe和Either表现不同.

例如:

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
Run Code Online (Sandbox Code Playgroud)

调用testMyDiv2 1 0给出结果"An error",但调用testMyDiv1 1 0给出:

"*** Exception: My divison by zero
Run Code Online (Sandbox Code Playgroud)

(注意缺少结束引用,表明这不是字符串而是例外).

是什么赋予了?

mok*_*kus 15

简短的回答是Haskell中的Monad类将fail操作添加到monad的原始数学概念中,这使得如何将Either类型转换为(Haskell)有点争议Monad,因为有很多方法可以做到这一点.

有几种实现浮动可以做不同的事情.我所知道的3种基本方法是:

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