Question

I'm trying to do what must be blindingly obvious in Haskell, which is go from Just [1] and Just [2] to Just [1, 2]. However I can't find anything online as I keep finding related but unhelpful pages. So, how do you achieve this?

Was it helpful?

Solution

You can use liftA2 (++):

liftA2 (++) :: Maybe [a] -> Maybe [a] -> Maybe [a]

liftA2 just lifts a binary function into an Applicative. Applicatives were designed for lifting functions of arbitrary arguments in a context, so they're perfect for this. In this case, the Applicative we're using is Maybe. To see how this works, we can look at the definition:

liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c
liftA2 f a b = f <$> a <*> b

(<$>) just lifts any function on pure values to one operating inside f: (a -> b) -> f a -> f b. (It's just an alias for fmap, if you're familiar with Functors.) For Maybe:

_ <$> Nothing = Nothing
f <$> Just x = Just (f x)

(<*>) is a bit trickier: it applies a function inside f to a value inside f: f (a -> b) -> f a -> f b. For Maybe:

Just f <*> Just x = Just (f x)
_ <*> _ = Nothing

(In fact, f <$> x is the same thing as pure f <*> x, which is Just f <*> x for Maybe.)

So, we can expand the definition of liftA2 (++):

liftA2 (++) a b = (++) <$> a <*> b

-- expand (<$>)
liftA2 (++) (Just xs) b = Just (xs ++) <*> b
liftA2 (++) _ _ = Nothing

-- expand (<*>)
liftA2 (++) (Just xs) (Just ys) = Just (xs ++ ys)
liftA2 (++) _ _ = Nothing

Indeed, we can use these operators to lift a function of any number of arguments into any Applicative, just by following the pattern of liftA2. This is called applicative style, and is very common in idiomatic Haskell code. In this case, it might even be more idiomatic to use it directly by writing (++) <$> a <*> b, if a and b are already variables. (On the other hand, if you're partially applying it — say, to pass it to a higher-order function — then liftA2 (++) is preferable.)

Every Monad is an Applicative, so if you ever find yourself trying to "lift" a function into a context, Applicative is probably what you're looking for.

OTHER TIPS

To expand the solution to a list of Justs, you could use

fmap join $ sequence [Just[1],Just[2],Just[3]]
-- Just [1,2,3]

while @ehird's answer is great, I would have used a noobish solution in the form:

mergeJust a b = do
    a' <- a
    b' <- b
    return (a' ++ b')

Since it was not mentioned in other solutions, I'll say it here. The simplest way to accomplish your task, in my opinion, is to use a <> (or mappend) from Data.Monoid.

import Data.Monoid

Just [1,2] <> Just [7,8] == Just [1,2,7,8]

However, note that this solution, unlike ehird's applicative solution, will not short-circuit on Nothing values.

Just [1,2] <> Nothing ---> Just [1,2]
--However
(++) <$> Just [1,2] <*> Nothing ---> Nothing
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top