New to Haskell and I can't figure out how apply a function (a -> b) into a list [Maybe a] and get [Maybe b]
maybeX:: (a -> b) -> [Maybe a] -> [Maybe b]
The function is supposed to do the exact same thing as map, apply the function f on a list of Maybe statements and if it Just it returns me a f Just and if it's a Nothing just a Nothing. Like the following example I want to add +5 on every Element of the following List :
[Just 1,Just 2,Nothing,Just 3]
and get
[Just 6,Just 7,Nothing,Just 8]
Really trying to figure this and I tried a lot but it always seems like it is something that I'm not aware of in the way of which this Maybe datatype works.. Thanks for your help!
Let's start by defining how to act on a single Maybe
, and then we'll scale it up to a whole list.
mapMaybe :: (a -> b) -> Maybe a -> Maybe b
mapMaybe f Nothing = Nothing
mapMaybe f (Just x) = Just (f x)
If the Maybe
contains a value, mapMaybe
applies f
to it, and if it doesn't contain a value then we just return an empty Maybe
.
But we have a list of Maybe
s, so we need to apply mapMaybe
to each of them.
mapMaybes :: (a -> b) -> [Maybe a] -> [Maybe b]
mapMaybes f ms = [mapMaybe f m | m <- ms]
Here I'm using a list comprehension to evaluate mapMaybe f m
for each m
in ms
.
Now for a more advanced technique. The pattern of applying a function to every value in a container is captured by the Functor
type class.
class Functor f where
fmap :: (a -> b) -> f a -> f b
A type f
is a Functor
if you can write a function which takes a function from a
to b
, and applies that function to an f
full of a
s to get an f
full of b
s. For example, []
and Maybe
are both Functor
s:
instance Functor Maybe where
fmap f Nothing = Nothing
fmap f (Just x) = Just (f x)
instance Functor [] where
fmap f xs = [f x | x <- xs]
Maybe
's version of fmap
is the same as the mapMaybe
I wrote above, and []
's implementation uses a list comprehension to apply f
to every element in the list.
Now, to write mapMaybes :: (a -> b) -> [Maybe a] -> [Maybe b]
, you need to operate on each item in the list using []
's version of fmap
, and then operate on the individual Maybe
s using Maybe
's version of fmap
.
mapMaybes :: (a -> b) -> [Maybe a] -> [Maybe b]
mapMaybes f ms = fmap (fmap f) ms
-- or:
mapMaybes = fmap . fmap
Note that we're actually calling two different fmap
implementations here. The outer one is fmap :: (Maybe a -> Maybe b) -> [Maybe a] -> [Maybe b]
, which dispatches to []
's Functor
instance. The inner one is (a -> b) -> Maybe a -> Maybe b
.
One more addendum - though this is quite esoteric, so don't worry if you don't understand everything here. I just want to give you a taste of something I think is pretty cool.
This "chain of fmap
s" style (fmap . fmap . fmap ...
) is quite a common trick for drilling down through multiple layers of a structure. Each fmap
has a type of (a -> b) -> (f a -> f b)
, so when you compose them with (.)
you're building a higher-order function.
fmap :: Functor g => (f a -> f b) -> (g (f a) -> g (f b))
fmap :: Functor f => (a -> b) -> (f a -> f b)
-- so...
fmap . fmap :: (Functor f, Functor g) => (a -> b) -> g (f a) -> g (f b)
So if you have a functor of functors (of functors...), then n fmap
s will let you map the elements at level n of the structure. Conal Elliot calls this style "semantic editor combinators".
The trick also works with traverse :: (Traversable t, Applicative f) => (a -> f b) -> (t a -> f (t b))
, which is a kind of "effectful fmap
".
traverse :: (...) => (t a -> f (t b)) -> (s (t a) -> f (s (t b)))
traverse :: (...) => (a -> f b) -> (t a -> f (t b))
-- so...
traverse . traverse :: (...) => (a -> f b) -> s (t a) -> f (s (t b))
(I omitted the bits before the =>
because I ran out of horizontal space.) So if you have a traversable of traversables (of traversables...), you can perform an effectful computation on the elements at level n just by writing traverse
n times. Composing traversals like this is the basic idea behind the lens
library.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With