【问题标题】:Is it possible to leverage monadic structure if multiple monads are "mixed"?如果多个单子“混合”,是否可以利用单子结构?
【发布时间】:2016-02-18 23:00:27
【问题描述】:

考虑以下代码:

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 是返回Maybe Int 的函数调用。

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

那么,我是否有可能利用 Maybe monad 的行为来避免出现阶梯状,同时仍然能够抛出异常?

【问题讨论】:

标签: haskell monads monad-transformers


【解决方案1】:

您似乎希望使用与哪个失败相关的信息来丰富一系列Maybe 操作(如果有)。为什么不将 enriching 实现为一个简单的函数?

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

我正在使用 MonadError - 单子类 m 可以抛出 e 类型的错误 - 泛化 enrich 的类型。例如,我们可以将其用作e -> Maybe a -> Except e ae -> Maybe a -> Either e a 类型,因为ExceptEither 都是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——也就是说,你没有使用早期的结果来确定以后的计算——有一种惯用的方法来推广这个函数来处理任意数量的@ 987654338@s。我们将把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)]

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

【讨论】:

  • 惯用语,我受不了zip [n..]。如果lens 差不多,我会使用itraverse。否则,我会为列表写自己的traverseWithIndex
  • @dfeuer 你不喜欢它什么?
  • 它并没有真正直接表达想法,而且对于列表融合也不是那么好(只有一个列表可以融合,而最近的GHC只有第一个)。
  • @dfeuer 我认为您在这种情况下是对的:将逻辑将错误消息模板化到enrichMaybes 中分散了说明traverse 的注意力。我已经相应地调整了答案。 (总的来说,我确实认为zip [1..] xs 是一种非常简洁的表达方式。)我在 Google 上搜索了一下,但找不到关于 为什么 zip 的任何讨论它的论点,你能指出我正确的方向吗?
  • 我相信只有一个人可以融合的原因在原始的“砍伐森林的捷径”论文中有所讨论。我成功游说停止尝试融合第二个参数(仅尝试第一个),因为尝试融合第二个参数可能会引入底部(这总是很糟糕)并且以脆弱的方式(尤其糟糕)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-11
  • 1970-01-01
  • 2020-06-25
  • 1970-01-01
  • 2014-10-20
  • 1970-01-01
  • 2020-06-05
相关资源
最近更新 更多