Reputation: 3121
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 Maybe
s. So it looks something like
Maybe
m1
traverse-like
an async operation over m1
m1
is Nothing
, short-circuit out of the computation, i.e. traverse-like
returns m2 == Nothing
to the computationm1
is Just x
, perform the async effect which may return async Nothing
or async Just x
, s.t. 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.
Upvotes: 0
Views: 146
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