简体   繁体   中英

What is This More General Almost-Traversal Operation

Trying to wrap my mind around traversals - or maybe something slightly different, in this case.

I understand a traversal to be an operation that performs an applicative operation (or effect) over a traversable structure. So for example from Data.Traversable ,

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

Where mapM is just a form of traverse for monads. So this takes a function g that returns a list ranging from 1 to the function's input, and a Maybe . The result of traversing this list-returning function over the Maybe Just is the Just constructor applied to each element of the list ranging from 1 to the 3 contained in the original Just . That's all well and good.

What I am wondering about is what if we generalize g a little bit. In a traversal, g takes a value of type a matching the type contained in the Traversable , and returns an applicative of a result type b :

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

What if instead, g does not return merely fb , but instead returns the same type f (Const mb) that the overall operation returns -

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

My specific context is this - I'm working in an application composing a series of Maybe computations with async effects, which operations themselves asynchronously return Maybe s. So it looks something like

  • start with a Maybe m1
  • traverse-like an async operation over m1
    • if m1 is Nothing , short-circuit out of the computation, ie traverse-like returns m2 == Nothing to the computation
    • if m1 is Just x , perform the async effect which may return async Nothing or async Just x , st traverse-like returns m2 == Nothing or Just async x
  • traverse-like an async operation over m2 ...

The main utility of this operation is the flattening a Maybe async Maybe (obtained from traversing an a -> async Maybe operation) into a Maybe async .

This whole world is new to me (coming from an imperative background), so I apologize if I'm not even thinking about this correctly - but I hope the above example at least makes it clear what I am trying to accomplish. It seems more general than a mere traversal, similarly to how bind is more general than map - but I don't know what the operation is actually called, or if it even makes sense beyond this particular use case. If anyone with more experience in functional programming, Haskell, etc. could shed light on the nature of this operation, I would appreciate it.

It's a little unclear to me from your description of your use-case if you are describing two calls to traverse-like (one to process m1 and another to process m2 ) or if you are describing a single traverse-like call that would recurse/iterate into processing m2 .

If it's the one-step, non-recursive version you're after, then it's possible that you're looking for:

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

When specialized to m ~ Maybe , it appears to do what you want:

traverse2 (...) Nothing = return Nothing
traverse2 (\i -> return Nothing) (Just 1) = return Nothing
traverse2 (\i -> return $ Just (i+1)) (Just 1) = return (Just 2)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM