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

时间:2016-02-09 09:01:15

标签: haskell monads

给出以下函数,该函数产生包含在Monad中的结果:

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

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

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

但是,对于Either Stringfail实际上会产生异常,但我希望它是Left并带有错误消息:

> ab 'c' :: Either String Bool
*** Exception: say what?

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

3 个答案:

答案 0 :(得分:5)

  

为什么?

请记住fail的类型:Monad m => String -> m a。现在,如果Either的monad实例仅为Either String定义,那么这很容易:

instance Monad (Either String) where
    fail = Left
    ...

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

instance Monad (Either e) where
    ...

因此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.)

由于e不限于String,因此没有通用的方法将错误消息存储在Left e中。例如,以下示例应该如何返回?

example :: Either () () 
example = fail "Example"

即使您使用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

请注意,有a proposalfailMonad类型类别拆分为MonadFail类型。

答案 1 :(得分:2)

在当天,当时“标准”库中没有Monad Either个实例。相反,此实例为defined in the mtl library。但是,要使fail正常工作,我们需要一种方法将字符串转换为eEither e类型的任何内容。这导致了Error课程。回到当天,mtl中的Monad实例将Error作为Either Monad实例的约束。这只是为了让fail做正确的事情,大多数人都认为failMonad课上有点蠢。不幸的是,这意味着您无法在Monad上使用任何Either操作,除非您的“错误”类型是Error的一个不典型或通常不合适的实例。 Either的孤儿实例在竞争对手Monad图书馆transformers出现时也造成了很多麻烦。但是,最终,无论“错误”类型和Either实例移动到基础的移动都使Monad上的monad操作有用,导致约束被移除。但是,现在没有明确的事情可以为fail定义。

答案 2 :(得分:1)

因为Either是一种脱节,而不仅仅是成功/错误。它通常用于表示成功/错误(因为它很方便),但一般情况并非如此。

这就是为什么数据构造函数被命名为LeftRight的原因。他们在代表价值方面的权利是平等的。

例如,我可以拥有Either Computation Result或类似的东西。 Either Gold Money等等。

如果fail最终返回分离的一个分支,则可能不正确。