考虑以下代码:
run = runExcept $ do
case Just 1 of
Nothing -> throwE "escape 1"
Just x -> do
case Just 2 of
Nothing -> throwE "escape 2"
Just y -> do
case Just 3 of
Nothing -> throwE "escape 3"
Just z -> return z
假设 Just 1
、Just 2
、Just 3
是返回 Maybe Int
的函数调用。
整个函数都在 ExceptT
内,因为我想抛出异常。但里面实际上只是很多被操纵的Maybe
值。
那么,我是否可以利用 Maybe
monad 的行为来避免楼梯外壳,同时仍然能够抛出异常?
最佳答案
您似乎想用与哪个失败(如果有)相关的信息来丰富一系列Maybe
操作。为什么不将丰富实现为一个简单的函数?
enrich :: MonadError e m => e -> Maybe a -> m a
enrich e Nothing = throwError e
enrich e (Just x) = return x
我正在使用MonadError
- monads m
类,可以抛出 e
类型的错误 - 泛化 enrich
的类型。例如,我们可以使用它,就好像它具有类型 e -> Maybe a -> except e a
或 e -> Maybe a -> Either e a
,因为 Except
和 Either
都是 MonadError
的实例。
现在您只需使用 enrich
将您的 Maybe
值一次提升到更丰富的一元上下文中即可。
action = do
x <- enrich "escape 1" maybe1 -- look mum, no staircasing!
y <- enrich "escape 2" maybe2
z <- enrich "escape 3" maybe3
return [x, y, z]
<小时/>
如果您应用地使用您的单子(monad) - 也就是说,您没有使用早期的结果来确定以后的计算 - 有一种惯用的方法可以将此函数推广到任意数量的 也许
。我们将把Maybes
以及我们需要丰富它的额外数据放入一个列表中 - 在本例中是错误消息。那么我们就可以traverse
(née mapM
)列表来丰富其中的每个也许
并将它们连接成一个更大的单子(monad) Action 。
enrichMaybes :: (Traversable t, MonadError e m) => t (e, Maybe a) -> m (t a)
enrichMaybes = traverse (uncurry enrich)
action = enrichMaybes [("escape 1", maybe1), ("escape 2", maybe2), ("escape 3", maybe3)]
能够像这样将效果视为一等公民,这就是为什么函数式编程是一件好事。
关于haskell - 如果多个 monad 为 "mixed",是否可以利用 Monadic 结构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35494202/