Monad结果类型不会在`fail`上产生`Either String`

Tim*_*mur 2 monads haskell

给定以下函数生成包含在Monad中的结果:

ab :: (Monad m) => Char -> m Bool
ab 'a' = return True
ab 'b' = return False
ab _   = fail "say what?"
Run Code Online (Sandbox Code Playgroud)

以下按照我的预期使用工作:

ab 'a' :: [Bool]      -- results in [True]
ab 'c' :: [Bool]      -- results in []
ab 'b' :: Maybe Bool  -- results in Just b
ab 'c' :: Maybe Bool  -- results in Nothing
ab 'a' :: Either String Bool  -- results in Right True
Run Code Online (Sandbox Code Playgroud)

但是,有Either String,fail实际上产生了异常,但我希望它是Left一个错误信息:

> ab 'c' :: Either String Bool
*** Exception: say what?
Run Code Online (Sandbox Code Playgroud)

为什么?有没有办法改变上面的代码(函数实现,或它的应用方式),以便Left在出现故障时生成(但肯定保持通用).

Zet*_*eta 5

为什么?

记住fail的类型:Monad m => String -> m a.现在,如果Either仅定义了monad实例Either String,这将很容易:

instance Monad (Either String) where
    fail = Left
    ...
Run Code Online (Sandbox Code Playgroud)

但是,实际情况更为通用:

instance Monad (Either e) where
    ...
Run Code Online (Sandbox Code Playgroud)

因此fail,即使我们将它约束到这个特定的实例,它的类型也更通用:

-- No          v  restriction on e   v
fail :: forall e a. String -> Either e a
--      ^^^^^^^^^^
-- This is implicitly there every time you use a polymorphic function
-- (unless you start toying around with some extensions and move it further
--  to the right or into parentheses, see RankNTypes or similar extensions.)
Run Code Online (Sandbox Code Playgroud)

并且由于e不限String于此,因此没有通用的方法来存储错误消息Left e.例如,以下示例应如何返回?

example :: Either () () 
example = fail "Example"
Run Code Online (Sandbox Code Playgroud)

即使您使用了Either String a,仍然会使用更一般的实例.另一种方法是使用拟合实例的newtype或您自己的ADT:

data EitherString a  = ELeft String | ERight a

instance Monad EitherString where
    fail = ELeft
    ...


newtype EitherWrap a = Wrapped { unWrap :: Either String a }

instance Monad EitherWrap where
    fail = Wrapped . Left
Run Code Online (Sandbox Code Playgroud)

需要注意的是有一个提议拆分failMonad类型类成MonadFail一个.