如果多个 monad 是 "mixed",是否可以利用 monadic 结构?

Is it possible to leverage monadic structure if multiple monads are "mixed"?

考虑以下代码:

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 1Just 2Just 3 是 return Maybe Int 的函数调用。

整个函数都在 ExceptT 中,因为我想抛出异常。但里面真的只是很多 Maybe 值被操纵。

那么,我是否可以利用 Maybe monad 的行为来避免楼梯外壳,同时仍然能够抛出异常?

您似乎想要使用有关哪个失败 的信息来丰富一系列 Maybe 操作,如果有的话。为什么不将 enriching 作为一个简单的函数来实现?

enrich :: MonadError e m => e -> Maybe a -> m a
enrich e Nothing = throwError e
enrich e (Just x) = return x

我正在使用 MonadError - monads m 的 class 可以抛出 e 类型的错误 - 以概括 enrich 的类型.例如,我们可以像使用 e -> Maybe a -> Except e ae -> Maybe a -> Either e a 类型一样使用它,因为 ExceptEither 都是 MonadError.[=32 的实例=]

现在您只需要使用 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 applicatively - 也就是说,你没有使用早期的结果来确定以后的计算 - 有一种惯用的方法来概括这个函数来处理任意数量的 Maybe。我们将把 Maybes 推入一个列表,连同我们需要的额外数据来丰富它——在本例中是错误消息。然后我们可以 traverse (née mapM) 列表来丰富其中的每个 Maybe 并将它们连接成一个更大的单子动作。

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)]

像这样将效果作为第一位 class 公民的能力就是为什么函数式编程是一件好事。