Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merging/Appending Justs in Haskell

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?

like image 386
Dean Barnes Avatar asked Jan 24 '12 18:01

Dean Barnes


3 Answers

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.

like image 173
ehird Avatar answered Oct 13 '22 12:10

ehird


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]
like image 31
Landei Avatar answered Oct 13 '22 10:10

Landei


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')
like image 44
Simon Bergot Avatar answered Oct 13 '22 12:10

Simon Bergot