-
Notifications
You must be signed in to change notification settings - Fork 52
Functors, Applicatives, And Monads In Pictures
This is a translation of Functors, Applicatives, And Monads In Pictures. This time we try and translate it from Haskell to Python.
Here’s a simple value:
And we know how to apply a function to this value:
Simple enough. Lets extend this by saying that any value can be in a context. For now you can think of a context as a box that you can put a value in:
Now when you apply a function to this value, you’ll get different results depending on the context. This is the idea that Functors, Applicatives, Monads, Arrows etc are all based on. The Maybe data type defines two related contexts:
class Maybe(ø.monad.Monad, ø.monoid.Monoid, ø.applicative.Applicative, ø.functor.Functor)
| The Maybe type encapsulates an optional value. A value of type Maybe a
| either contains a value of (represented as Just a), or it is empty
| (represented as Nothing). Using Maybe is a good way to deal with errors or
| exceptional cases without resorting to drastic measures such as error.
|
In a second we’ll see how function application is different when something is a Just a versus a Nothing. First let’s talk about Functors!
When a value is wrapped in a context, you can’t apply a normal function to it:
This is where fmap comes in. fmap is from the street, fmap is hip to contexts. fmap knows how to apply functions to values that are wrapped in a context. For example, suppose you want to apply (+3) to Just 2. Use fmap:
> Just(2).fmap(lambda x: x+3)
Just 5Bam! fmap shows us how it’s done! But how does fmap know how to apply the function?
Functor is a typeclass. Here’s the definition:
A Functor is any data type that defines how fmap applies to it. Here’s how fmap works:
So we can do this:
> Just(2).fmap(lambda x: x+3)
Just 5And fmap magically applies this function, because Maybe is a Functor. It specifies how fmap applies to Justs and Nothings:
class Just(Maybe):
def fmap(self, mapper) -> Maybe:
value = self._get_value()
try:
result = mapper(value)
except TypeError:
result = partial(mapper, value)
return Just(result)
class Nothing(Maybe):
def fmap(self, _) -> Maybe:
return Nothing()






