0

试图将我的思想围绕遍历 - 或者在这种情况下可能略有不同。

我将遍历理解为对可遍历结构执行应用操作(或效果)的操作。因此,例如从Data.Traversable

>>> mapM (\n -> [1..n]) $ Just 3
[Just 1,Just 2,Just 3]

wheremapM只是traversefor monads 的一种形式。所以这需要一个函数g,它返回一个从 1 到函数输入的列表,以及一个Maybe. 在 上遍历这个列表返回函数的结果Maybe JustJust应用于列表中每个元素的构造函数,范围从 1 到3原始 中包含的Just。这一切都很好。

我想知道的是,如果我们稍微概括一下会怎样g。在遍历中,g获取a与 中包含的类型匹配的类型值Traversable,并返回结果类型的应用程序b

traverse :: Applicative f => (a -> f b) -> Const m a -> f (Const m b)
                             ----------

相反,如果不是g仅返回f b,而是返回与f (Const m b)整个操作返回的类型相同的类型,该怎么办 -

?traverse-like? :: Applicative f => (a -> f (Const m b)) -> Const m a -> f (Const m b)
                                    --------------------

我的具体上下文是这样的 - 我正在一个应用程序中工作,该应用程序由一系列Maybe具有异步效果的计算组成,这些操作本身异步返回Maybes。所以它看起来像

  • 从一个开始Maybe m1
  • traverse-like一个异步操作m1
    • 如果m1Nothing,则计算短路,即traverse-like返回m2 == Nothing计算
    • 如果m1Just x,执行可能返回async Nothing或的异步效果async Just x,sttraverse-like返回m2 == NothingJust async x
  • traverse-like一个异步操作m2...

此操作的主要用途是将 a Maybe async Maybe(通过遍历a -> async Maybe操作获得)展平为 a Maybe async

整个世界对我来说都是新的(来自命令式背景),所以如果我什至没有正确考虑这一点,我深表歉意 - 但我希望上面的例子至少能清楚地说明我想要完成的事情。它似乎比单纯的遍历更通用,类似于 how bindis more general than map- 但我不知道实际调用的操作是什么,或者它是否在这个特定用例之外有意义。如果在函数式编程、Haskell 等方面有更多经验的人能够阐明这种操作的本质,我将不胜感激。

4

1 回答 1

1

从您对用例的描述中,我有点不清楚您是在描述两个调用traverse-like(一个到 process m1,另一个到 process m2),或者如果您正在描述一个traverse-like会递归/迭代到 processing 的调用m2

如果它是您所追求的一步式非递归版本,那么您可能正在寻找:

traverse2 :: (Applicative f, Monad m, Traversable m) => (a -> f (m b)) -> m a -> f (m b)
traverse2 f = fmap join . traverse f

当专门用于 时m ~ Maybe,它似乎可以做你想做的事:

traverse2 (...) Nothing = return Nothing
traverse2 (\i -> return Nothing) (Just 1) = return Nothing
traverse2 (\i -> return $ Just (i+1)) (Just 1) = return (Just 2)
于 2022-02-23T21:43:11.543 回答