给出以下函数,该函数产生包含在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 String
,fail
实际上会产生异常,但我希望它是Left
并带有错误消息:
> ab 'c' :: Either String Bool
*** Exception: say what?
为什么?有没有办法改变上面的代码(函数实现,或它的应用方式),以便在出现故障时生成Left
(但肯定保持通用)。
答案 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 proposal个fail
从Monad
类型类别拆分为MonadFail
类型。
答案 1 :(得分:2)
在当天,当时“标准”库中没有Monad
Either
个实例。相反,此实例为defined in the mtl library。但是,要使fail
正常工作,我们需要一种方法将字符串转换为e
中Either e
类型的任何内容。这导致了Error
课程。回到当天,mtl中的Monad
实例将Error
作为Either
Monad
实例的约束。这只是为了让fail
做正确的事情,大多数人都认为fail
在Monad
课上有点蠢。不幸的是,这意味着您无法在Monad
上使用任何Either
操作,除非您的“错误”类型是Error
的一个不典型或通常不合适的实例。 Either
的孤儿实例在竞争对手Monad图书馆transformers出现时也造成了很多麻烦。但是,最终,无论“错误”类型和Either
实例移动到基础的移动都使Monad
上的monad操作有用,导致约束被移除。但是,现在没有明确的事情可以为fail
定义。
答案 2 :(得分:1)
因为Either
是一种脱节,而不仅仅是成功/错误。它通常用于表示成功/错误(因为它很方便),但一般情况并非如此。
这就是为什么数据构造函数被命名为Left
和Right
的原因。他们在代表价值方面的权利是平等的。
例如,我可以拥有Either Computation Result
或类似的东西。 Either Gold Money
等等。
如果fail
最终返回分离的一个分支,则可能不正确。