Reite's answer is correct, and it's generally how I'd normally recommend handling it - however, it seems to me that you don't quite understand how to work with Maybe
values; if so there is little sense looking at applicative functors right now.
The definition of Maybe
is basically just
data Maybe a = Nothing | Just a
Which basically means exactly what it sounds like when you read it in plain english "A value of type Maybe a
is either the value Nothing
for that type, or a value of the form Just a
".
Now you can use pattern matching to work with that, with the example of lists:
maybeReverse :: Maybe [a] -> Maybe [a]
maybeReverse Nothing = Nothing
maybeReverse (Just xs) = Just $ reverse xs
Which basically means "If the value is Nothing
, then there's nothing to reverse, so the result is Nothing
again. If the value is Just xs
then we can reverse xs
and wrap it with Just
again to turn it into a Maybe [a]
value).
Of course writing functions like this for every single function we ever want to use with a Maybe
value would be tedious; So higher order functions to the rescue! The observation here is that in maybeReverse
we didn't do all that much with reverse
, we just applied it to the contained value and wrapped the result of that in Just
.
So we can write a function called liftToMaybe
that does this for us:
liftToMaybe :: (a->b) -> Maybe a -> Maybe b
liftToMaybe f Nothing = Nothing
liftToMaybe f (Just a) = Just $ f a
A further observation we can make is that because functions are values, we can also have Maybe
values of functions. To do anything useful with those we could again unwrap them... or notice we're in the same situation as in the last paragraph, and immediately notice that we don't really care what function exactly is in that Maybe
value and just write the abstraction directly:
maybeApply :: Maybe (a->b) -> Maybe a -> Maybe b
maybeApply Nothing _ = Nothing
maybeApply _ Nothing = Nothing
maybeApply (Just f) (Just a) = Just $ f a
Which, using our liftToMaybe
function above, we can simplify a bit:
maybeApply :: Maybe (a->b) -> Maybe a -> Maybe b
maybeApply Nothing _ = Nothing
maybeApply (Just f) x = liftToMaybe f x
The <$>
and <*>
operators in Reite's answer are basically just infix names for liftToMaybe
(which is also known as fmap
) and maybeApply
respectively; They have the types
(<$>) :: Functor f => (a->b) -> f a -> f b
(<*>) :: Applicative f => f (a->b) -> f a -> f b
You don't really need to know what the Functor
and Applicative
things are right now (although you should look into them at some point; They're basically generalizations of the above Maybe
functions for other kinds of "context") - basically, just replace f
with Maybe
and you'll see that these are basically the same functions we've talked about earlier.
Now, I leave applying this to your original problem of multiplication to you (although the other answers kinda spoil it).