How do I subtract Maybe values in Haskell? For example, if I want to subtract Just 8 - Just 5 to get Just 3, how would I do that?

Viewed 507

How can I do the following. For example, if I want to subtract Just 8 - Just 5 to get Just 3, how would I do that?

Just 8 - Just 5 = Just 3

Just 15 - Just 9 = Just 6
4 Answers

The liftA2 function (imported from Control.Applicative) "lifts" an ordinary function into an Applicative context. liftA2 (-), for example, is a function that takes not two Num a => a values, but two (Applicative f, Num a) => f a values, and produces a result in the same context. This works not just for Maybe:

>>> liftA2 (-) (Just 8) (Just 5)
Just 3

but for lists

>>> liftA2 (-) [4,5,6] [1,2,3] -- difference of every pair with one number from each list
[3,2,1,4,3,2,5,4,3]    

Either:

>>> liftA2 (-) (Right 8) (Right 5)
Right 3

functions

>>> liftA2 (-) (+3) (*2) 9 -- (\x -> (x + 3) - (x * 2)) 9 == 12 - 18
-6

etc.

import Control.Applicative

main = print $ liftA2 (-) (Just 8) (Just 5)

Use monad comprehensions:

{-# LANGUAGE MonadComprehensions #-}

foo :: (Num b, Monad m) => m b -> m b -> m b
foo a b = [ x - y | x <- a, y <- b ]

Nice, visual, underrated.

foo (Just 8) (Just 5) returns Just 3.

Of course in such a simple case it is a syntactical overkill, you're much better off with liftA2 (-) both semantically and practically.

Where monad comprehensions really shine is when you have some conditional computation. The classic example is safe division, avoiding the divide-by-zero errors:

safediv :: (MonadPlus m, Fractional b, Eq b) 
        => m b -> m b -> m b
safediv a b = [ x / y | x <- a, y <- b, y /= 0 ]

-- safediv (Just 8) (Just 0)  returns  Nothing

This is an essentially monadic operation, it can't be coded with applicatives, and any other alternative way to write it down is bound to be more verbose.

In general you are expected to use a monadic approach:

Just 8 >>= \n -> Just 5 >>= \m -> return (n-m)

However without getting into monads we might still do this job by using functor and applicative instances of Maybe type.

First let's fmap the (-) operator over Just 8 value, like (-) <$> Just 8. This would result in an applicative of Maybe type which is Just (8-) with type signature Just (8-) :: Num a => Maybe (a -> a). Now it just takes a single applicative operation to complete the job:

λ> (-) <$> (Just 8) <*> (Just 5) 
Just 3

This is sometimes done by LiftA2 as shown in some other answers but I think it's better to know this pattern in the first place since LiftA2 is hardwired to binary operators / functions, in other words if you have to lift a ternary operator such as (,,) then you need to use LiftA3 instead. However the above pattern simply does it in the same way, like

λ> (,,) <$> Just 1 <*> Just 't' <*> Just "yeah"
Just (1,'t',"yeah")
Related