Bondolin
Bondolin

Reputation: 3121

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 f b, but instead returns the same type f (Const m b) 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 Maybes. So it looks something like

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.

Upvotes: 0

Views: 146

Answers (1)

K. A. Buhr
K. A. Buhr

Reputation: 50819

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)

Upvotes: 1

Related Questions