Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

apply function on Maybe types?

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!

like image 408
SomaXD Avatar asked Nov 26 '17 17:11

SomaXD


1 Answers

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 Maybes, 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 as to get an f full of bs. For example, [] and Maybe are both Functors:

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 Maybes 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 fmaps" 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 fmaps 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.

like image 56
Benjamin Hodgson Avatar answered Sep 30 '22 11:09

Benjamin Hodgson