请考虑以下代码:
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的行为来避免阶梯套管,同时还能抛出异常?
答案 0 :(得分:6)
您似乎希望使用与哪些失败相关的信息来丰富一系列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
值提升到更丰富的monadic上下文中。
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 应用 - 也就是说,你没有使用早期的结果来确定以后的计算 - 有一种惯用的方法来推广这个函数来处理任意数量的{ {1}}秒。我们将把Maybe
推入列表,以及我们需要的额外数据来丰富它 - 在这种情况下,错误消息。然后我们可以traverse
(néemapM
)列表来丰富其中的每个Maybes
并将它们加入到更大的monadic动作中。
Maybe
将效果视为一流公民的能力就是为什么功能性编程是一件好事。