给定以下函数生成包含在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在出现故障时生成(但肯定保持通用).
为什么?
记住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)
需要注意的是有一个提议拆分fail从Monad类型类成MonadFail一个.
| 归档时间: |
|
| 查看次数: |
191 次 |
| 最近记录: |