Q: When is a function not a function?
A: When it is a method.
We’re talking here about functions and not methods. For the purposes of this discussion, a function:
ato some output
a, there is only one output
bmay be mapped to by more than one
Contrast this with a method:
bfor an input
Let’s look at a simple example function. You want to take your cat to the vet to get a shot for it.
Here we have a simple function from Cat to Cat. We input a cat
a on the left,
and we get out an immunized cat
b on the right.
It’s important to note that we should not mutate the input cat. What should happen
instead is we input cat
a on the left, and get out a brand new cat that is immunized.
a can be made available for garbage collection.
Of course, there’s no guarantee that people coming into the vet’s office have a cat–or any pet at all,
for that matter. If someone brings a
null where we are expecting a Cat, then our nice function from
Cat to Cat doesn’t work so well:
We could have the vet ask everything that comes by if it is
null or not. But that is not his job, really. His
job is giving shots to pets. I don’t want all of my functions littered with
null checks. Surely, there must be
some way to keep my functions clean and simple, yet make them
null-safe as well?
The answer is “Maybe”–more specifically, the “Maybe Functor”.
Note that when you are composing functions, all the functions in the composition have to
fulfill their contracts. If somebody returns a
null in there, the whole daisy chain is borked. Consider:
What happens if the call to
getElementById doesn’t find anything? You get a
null back. Then
null gets fed to
R.prop will fail.
To get around this problem, we can use a Functor.
A Functor is an object that can be mapped over. By “map” I mean that we can use the same function from Cat to Cat even though the Cat is now inside a container. Note that in the example above, The cat is in the kitty carrier, but we we are still using the same immunization function.
This is a big deal, because it allows us keep the
null-checking separate from the internal logic of
the function. Now if we get an empty container for input, then there is no reason to even try to apply the
function to its content. We simply get an empty container back.
This example is essentially the “Maybe” functor. “Maybe” let’s you wrap up a function from
so that you can apply it to the contents of the Maybe. Here’s how we have implemented that in Ramda:
(The Haskell folks may cringe at this–the definition of Maybe in Haskell is
data Maybe a = Nothing | Just a.
Now let’s go back to the
capitalize example. We can rewrite that to make use of the Maybe functor:
Now this function is guaranteed to output something–Maybe. If the call to
we’ll wind up with a
Maybe null at the end; otherwise we’ll have a
Maybe String. We didn’t have
to guard inside our functions, so that is nice. Instead, we have wrapped up the value we are composing
over inside this container. So we can only access the value by mapping over the container.
value is sitting there as a public property on
you could just query it. But then you are right back where you started, needing to test for
The Maybe functor only gives us one bit of information: Did we hit a
null somewhere along the way or not?
There are other functors that can wrap up more info. The Either functor is either a Left or a Right;
In the Left you can put an error message (or a function, or whatever). If it is a Right, then everything
Promises can also be looked at as functors. Consider:
There are many, many more ways to skin this cat: Validation, IO, Reader, Writer, State, Future … and of course Array.
Arrays are Functors. After all, it’s a container you can map over. Arrays are a bit trickier, though, since they are intertwined with the notion of iteration.
No reason to stop here, though. We can wrap up the object we’re mapping over in a Functor. We can also wrap up the function we want to apply inside a container:
Once again, it’s the same function from Cat to Cat, but now both function and data are wrapped in containers. The good folks
from Fantasy Land call an object that can handle this
situation an “Apply”, and the function you
use to apply a wrapped function to a wrapped value is
ap. For Maybe, that could look like this:
The implementation of
ap for Array is a bit more complicated, of course, because of iteration:
Maybe you are asking yourself, “How do you get into a situation where you have a function inside a container?”
I covered such a situation recently. That article also covers the
ability to create an Applicative, by wrapping up an arbitrary object inside a container via
Here’s how we implemented
And the implementation for Array is trivial:
Please read my article on Applicatives for a real-world example of these concepts in action. Gleb Bahmutov has also written a good article on using Applicative Functors with Promises that goes more in depth in the code than this article did. And I also should mention the excellent illustrated article Functors, Applicatives, And Monads In Pictures, by Aditya Y. Bhargava.
Thanks to J. C. Phillipps for the illustrations. No animals were harmed in the writing of this blog post.26 October 2014