Professor Frisby's Mostly Adequate Guide To Functional Programming
Professor Frisby's Mostly Adequate Guide To Functional Programming
Introduction 1.1
Chapter 1: What ever are we doing? 1.2
Introductions 1.2.1
A brief encounter 1.2.2
Chapter 2: First Class Functions 1.3
A quick review 1.3.1
Why favor first class? 1.3.2
Chapter 3: Pure Happiness with Pure Functions 1.4
Oh to be pure again 1.4.1
Side effects may include... 1.4.2
8th grade math 1.4.3
The case for purity 1.4.4
In Summary 1.4.5
Chapter 4: Currying 1.5
Can't live if livin' is without you 1.5.1
More than a pun / Special sauce 1.5.2
In Summary 1.5.3
Chapter 5: Coding by Composing 1.6
Functional Husbandry 1.6.1
Pointfree 1.6.2
Debugging 1.6.3
Category Theory 1.6.4
In Summary 1.6.5
Chapter 6: Example Application 1.7
Declarative Coding 1.7.1
A flickr of functional programming 1.7.2
A Principled Refactor 1.7.3
In Summary 1.7.4
Chapter 7: Hindley-Milner and Me 1.8
What's your type? 1.8.1
Tales from the cryptic 1.8.2
Narrowing the possibility 1.8.3
Free as in theorem 1.8.4
In Summary 1.8.5
Chapter 8: Tupperware 1.9
The Mighty Container 1.9.1
My First Functor 1.9.2
Schrödinger’s Maybe 1.9.3
Pure Error Handling 1.9.4
Old McDonald had Effects… 1.9.5
Asynchronous Tasks 1.9.6
A Spot of Theory 1.9.7
In Summary 1.9.8
Chapter 9: Monadic Onions 1.10
Pointy Functor Factory 1.10.1
Mixing Metaphors 1.10.2
My chain hits my chest 1.10.3
Theory 1.10.4
In Summary 1.10.5
Chapter 10: Applicative Functors 1.11
Applying Applicatives 1.11.1
Ships in bottles 1.11.2
Coordination Motivation 1.11.3
Bro, do you even lift? 1.11.4
Free can openers 1.11.5
Laws 1.11.6
In Summary 1.11.7
About this book
This is a book on the functional paradigm in general. We'll use the world's most
popular functional programming language: JavaScript. Some may feel this is a
poor choice as it's against the grain of the current culture which, at the moment,
feels predominately imperative. However, I believe it is the best way to learn FP
for several reasons:
This makes it possible to practice and apply your acquired knowledge each
day on real world programs rather than pet projects on nights and weekends
in an esoteric FP language.
We have all the features we need to mimic a language like Scala or Haskell
with the help of a tiny library or two. Object-oriented programming
currently dominates the industry, but it's clearly awkward in JavaScript. It's
akin to camping off of a highway or tap dancing in galoshes. We have to
bind all over the place lest this change out from under us, we don't
have classes (yet), we have various work arounds for the quirky behavior
when the new keyword is forgotten, private members are only available
via closures. To a lot of us, FP feels more natural anyways.
That said, typed functional languages will, without a doubt, be the best place to
code in the style presented by this book. JavaScript will be our means of learning
a paradigm, where you apply it is up to you. Luckily, the interfaces are
mathematical and, as such, ubiquitous. You'll find yourself at home with swiftz,
scalaz, haskell, purescript, and other mathematically inclined environments.
Do it yourself
cd mostly-adequate-guide/
npm install gitbook-cli -g
gitbook init
brew update
brew install Caskroom/cask/calibre
Contributing
See CONTRIBUTING.md
Translations
See TRANSLATIONS.md
FAQ
See FAQ.md
Plans for the future
Part 1 (currently chapters 1-7) is a guide to the basics. I'm updating as I
find errors since this is the initial draft. Feel free to help!
Part 2 (currently chapters 8+) will address type classes like functors and
monads all the way through to traversable. I hope to squeeze in
transformers and a pure application.
Part 3 will start to dance the fine line between practical programming and
academic absurdity. We'll look at comonads, f-algebras, free monads,
yoneda, and other categorical constructs.
Chapter 1: What ever are we doing?
Introductions
Hi there! I'm Professor Franklin Risby. Pleased to make your acquaintance. We'll
be spending some time together, as I'm supposed to teach you a bit about
functional programming. But enough about me, what about you? I'm hoping that
you're at least a bit familiar with the JavaScript language, have a teensy bit of
Object-Oriented experience, and fancy yourself a working class programmer.
You don't need to have a PhD in Entomology, you just need to know how to find
and kill some bugs.
I won't assume that you have any previous functional programming knowledge,
because we both know what happens when you assume. I will, however, expect
you to have run into some of the unfavorable situations that arise when working
with mutable state, unrestricted side effects, and unprincipled design. Now that
we've been properly introduced, let's get on with it.
The purpose of this chapter is to give you a feel for what we're after when we
write functional programs. In order to be able to understand the following
chapters, we must have some idea about what makes a program functional.
Otherwise we'll find ourselves scribbling aimlessly, avoiding objects at all costs -
a clumsy endeavor indeed. We need a clear bullseye to hurl our code at, some
celestial compass for when the waters get rough.
Now, there are some general programming principles - various acronymic credos
that guide us through the dark tunnels of any application: DRY (don't repeat
yourself), YAGNI (ya ain't gonna need it), loose coupling high cohesion, the
principle of least surprise, single responsibility, and so on.
I won't belabor you by listing each and every guideline I've heard throughout the
years... The point of the matter is that they hold up in a functional setting,
although they're merely tangential to our ultimate goal. What I'd like you to get a
feel for now, before we get any further, is our intention when we poke and prod
at the keyboard; our functional Xanadu.
A brief encounter
Let's start with a touch of insanity. Here is a seagull application. When flocks
conjoin they become a larger flock, and when they breed, they increase by the
number of seagulls with whom they're breeding. Now, this is not intended to be
good Object-Oriented code, mind you, it is here to highlight the perils of our
modern, assignment based approach. Behold:
Flock.prototype.conjoin = function(other) {
this.seagulls += other.seagulls;
return this;
};
Flock.prototype.breed = function(other) {
this.seagulls = this.seagulls * other.seagulls;
return this;
};
If you don't understand this program, it's okay, neither do I. The point to
remember here is that state and mutable values are hard to follow, even in such a
small example.
var flock_a = 4;
var flock_b = 2;
var flock_c = 0;
Well, this time we got the right answer. With much less code. The function
nesting is a tad confusing... (we'll remedy this situation in ch5). It's better, but
let's dig a little bit deeper. There are benefits to calling a spade a spade. Had we
scrutinized our custom functions more closely, we would have discovered that
we're just working with simple addition ( conjoin ) and multiplication
( breed ).
There's really nothing special at all about these two functions other than their
names. Let's rename our custom functions to multiply and add in order to
reveal their true identities.
var flock_a = 4;
var flock_b = 2;
var flock_c = 0;
// associative
add(add(x, y), z) === add(x, add(y, z));
// commutative
add(x, y) === add(y, x);
// identity
add(x, 0) === x;
// distributive
multiply(x, add(y,z)) === add(multiply(x, y), multiply(x, z));
Ah yes, those old faithful mathematical properties should come in handy. Don't
worry if you didn't know them right off the top of your head. For a lot of us, it's
been a while since we learned about these laws of arithmetic. Let's see if we can
use these properties to simplify our little seagull program.
// Original line
add(multiply(flock_b, add(flock_a, flock_c)), multiply(flock_a,
flock_b));
Brilliant! We didn't have to write a lick of custom code other than our calling
function. We include add and multiply definitions here for completeness,
but there is really no need to write them - we surely have an add and
multiply provided by some existing library.
You may be thinking "how very strawman of you to put such a mathy example
up front". Or "real programs are not this simple and cannot be reasoned about in
such a way." I've chosen this example because most of us already know about
addition and multiplication, so it's easy to see how math is very useful for us
here.
Don't despair - throughout this book, we'll sprinkle in some category theory, set
theory, and lambda calculus and write real world examples that achieve the same
elegant simplicity and results as our flock of seagulls example. You needn't be a
mathematician either. It will feel natural and easy, just like you were using a
"normal" framework or API.
It may come as a surprise to hear that we can write full, everyday applications
along the lines of the functional analog above. Programs that have sound
properties. Programs that are terse, yet easy to reason about. Programs that don't
reinvent the wheel at every turn. Lawlessness is good if you're a criminal, but in
this book, we'll want to acknowledge and obey the laws of math.
We'll want to use a theory where every piece tends to fit together so politely.
We'll want to represent our specific problem in terms of generic, composable bits
and then exploit their properties for our own selfish benefit. It will take a bit
more discipline than the "anything goes" approach of imperative programming
(we'll go over the precise definition of "imperative" later in the book, but for
now consider it anything other than functional programming). The payoff of
working within a principled, mathematical framework will truly astound you.
We've seen a flicker of our functional northern star, but there are a few concrete
concepts to grasp before we can really begin our journey.
A quick review
When we say functions are "first class", we mean they are just like everyone
else... so in other words a normal class. We can treat functions like any other
data type and there is nothing particularly special about them - they may be
stored in arrays, passed around as function parameters, assigned to variables, and
what have you.
That is JavaScript 101, but worth mentioning since a quick code search on
github will reveal the collective evasion, or perhaps widespread ignorance of this
concept. Shall we go for a feigned example? We shall.
var hi = function(name) {
return 'Hi ' + name;
};
hi('jonas');
// "Hi jonas"
Since greeting is merely in turn calling hi with the very same argument, we
could simply write:
greeting('times');
// "Hi times"
In other words, hi is already a function that expects one argument, why place
another function around it that simply calls hi with the same bloody
argument? It doesn't make any damn sense. It's like donning your heaviest parka
in the dead of July just to blast the air and demand an ice lolly.
A solid understanding of this is critical before moving on, so let's examine a few
more fun examples excavated from the library of npm packages.
// ignorant
var getServerStuff = function(callback) {
return ajaxCall(function(json) {
return callback(json);
});
};
// enlightened
var getServerStuff = ajaxCall;
The world is littered with ajax code exactly like this. Here is the reason both are
equivalent:
// this line
return ajaxCall(function(json) {
return callback(json);
});
// so refactor getServerStuff
var getServerStuff = function(callback) {
return ajaxCall(callback);
};
And that, folks, is how it is done. Once more so that we understand why I'm
being so persistent.
return {
index: index,
show: show,
create: create,
update: update,
destroy: destroy,
};
})();
var BlogController = {
index: Views.index,
show: Views.show,
create: Db.create,
update: Db.update,
destroy: Db.destroy,
};
... or scrap it altogether since it does nothing more than just bundle our Views
and Db together.
Why favor first class?
Okay, let's get down to the reasons to favor first class functions. As we saw in
the getServerStuff and BlogController examples, it's easy to add layers of
indirection that provide no added value and only increase the amount of
redundant code to maintain and search through.
httpGet('/post/2', function(json) {
return renderPost(json);
});
Had we written it as a first class function, much less would need to change:
Having multiple names for the same concept is a common source of confusion in
projects. There is also the issue of generic code. For instance, these two
functions do exactly the same thing, but one feels infinitely more general and
reusable:
By using specific naming, we've seemingly tied ourselves to specific data (in this
case articles ). This happens quite a bit and is a source of much reinvention.
I must mention that, just like with Object-Oriented code, you must be aware of
this coming to bite you in the jugular. If an underlying function uses this
and we call it first class, we are subject to this leaky abstraction's wrath.
var fs = require('fs');
// scary
fs.readFile('freaky_friday.txt', Db.save);
// less so
fs.readFile('freaky_friday.txt', Db.save.bind(Db));
Having been bound to itself, the Db is free to access its prototypical garbage
code. I avoid using this like a dirty nappy. There's really no need when
writing functional code. However, when interfacing with other libraries, you
might have to acquiesce to the mad world around us.
Some will argue that this is necessary for optimizing speed. If you are the
micro-optimization sort, please close this book. If you cannot get your money
back, perhaps you can exchange it for something more fiddly.
Oh to be pure again
One thing we need to get straight is the idea of a pure function.
A pure function is a function that, given the same input, will always return
the same output and does not have any observable side effect.
Take slice and splice . They are two functions that do the exact same thing
- in a vastly different way, mind you, but the same thing nonetheless. We say
slice is pure because it returns the same output per input every time,
guaranteed. splice , however, will chew up its array and spit it back out
forever changed which is an observable effect.
// pure
xs.slice(0, 3);
//=> [1, 2, 3]
xs.slice(0, 3);
//=> [1, 2, 3]
xs.slice(0, 3);
//=> [1, 2, 3]
// impure
xs.splice(0, 3);
//=> [1, 2, 3]
xs.splice(0, 3);
//=> [4, 5]
xs.splice(0, 3);
//=> []
// impure
var minimum = 21;
// pure
var checkAge = function(age) {
var minimum = 21;
return age >= minimum;
};
In the impure portion, checkAge depends on the mutable variable minimum to
determine the result. In other words, it depends on system state which is
disappointing because it increases the cognitive load by introducing an external
environment.
It might not seem like a lot in this example, but this reliance upon state is one of
the largest contributors to system
complexity(http://www.curtclifton.net/storage/papers/MoseleyMarks06a.pdf).
This checkAge may return different results depending on factors external to
input, which not only disqualifies it from being pure, but also puts our minds
through the ringer each time we're reasoning about the software.
Its pure form, on the other hand, is completely self sufficient. We can also make
minimum immutable, which preserves the purity as the state will never change.
To do this, we must create an object to freeze.
There's nothing intrinsically bad about effects and we'll be using them all over
the place in the chapters to come. It's that side part that bears the negative
connotation. Water alone is not an inherent larvae incubator, it's the stagnant part
that yields the swarms, and I assure you, side effects are a similar breeding
ground in your own programs.
It is not that we're forbidden to use them, rather we want to contain them and run
them in a controlled way. We'll learn how to do this when we get to functors and
monads in later chapters, but for now, let's try to keep these insidious functions
separate from our pure ones.
Side effects disqualify a function from being pure. And it makes sense: pure
functions, by definition, must always return the same output given the same
input, which is not possible to guarantee when dealing with matters outside our
local function.
Let's take a closer look at why we insist on the same output per input. Pop your
collars, we're going to look at some 8th grade math.
8th grade math
From mathisfun.com:
In other words, it's just a relation between two values: the input and the output.
Though each input has exactly one output, that output doesn't necessarily have to
be unique per input. Below shows a diagram of a perfectly valid function from
x to y ;
(http://www.mathsisfun.com/sets/function.html)
To contrast, the following diagram shows a relation that is not a function since
the input value 5 points to several outputs:
(http://www.mathsisfun.com/sets/function.html)
Functions can be described as a set of pairs with the position (input, output):
[(1,2), (3,6), (5,10)] (It appears this function doubles its input).
Or perhaps a table:
Input Output
1 2
2 4
3 6
There's no need for implementation details if the input dictates the output. Since
functions are simply mappings of input to output, one could simply jot down
object literals and run them with [] instead of () .
var toLowerCase = {
'A': 'a',
'B': 'b',
'C': 'c',
'D': 'd',
'E': 'e',
'F': 'f',
};
toLowerCase['C'];
//=> 'c'
var isPrime = {
1: false,
2: true,
3: true,
4: false,
5: true,
6: false,
};
isPrime[3];
//=> true
Of course, you might want to calculate instead of hand writing things out, but
this illustrates a different way to think about functions. (You may be thinking
"what about functions with multiple arguments?". Indeed, that presents a bit of
an inconvenience when thinking in terms of mathematics. For now, we can
bundle them up in an array or just think of the arguments object as the input.
When we learn about currying, we'll see how we can directly model the
mathematical definition of a function.)
Here comes the dramatic reveal: Pure functions are mathematical functions and
they're what functional programming is all about. Programming with these little
angels can provide huge benefits. Let's look at some reasons why we're willing
to go to great lengths to preserve purity.
The case for purity
Cacheable
For starters, pure functions can always be cached by input. This is typically done
using a technique called memoization:
squareNumber(4);
//=> 16
squareNumber(5);
//=> 25
return function() {
var arg_str = JSON.stringify(arguments);
cache[arg_str] = cache[arg_str] || f.apply(f, arguments);
return cache[arg_str];
};
};
Something to note is that you can transform some impure functions into pure
ones by delaying evaluation:
The interesting thing here is that we don't actually make the http call - we instead
return a function that will do so when called. This function is pure because it will
always return the same output given the same input: the function that will make
the particular http call given the url and params .
Our memoize function works just fine, though it doesn't cache the results of the
http call, rather it caches the generated function.
This is not very useful yet, but we'll soon learn some tricks that will make it so.
The takeaway is that we can cache every function no matter how destructive
they seem.
Portable / Self-Documenting
Pure functions are completely self contained. Everything the function needs is
handed to it on a silver platter. Ponder this for a moment... How might this be
beneficial? For starters, a function's dependencies are explicit and therefore
easier to see and understand - no funny business going on under the hood.
//impure
var signUp = function(attrs) {
var user = saveUser(attrs);
welcomeUser(user);
};
//pure
var signUp = function(Db, Email, attrs) {
return function() {
var user = saveUser(Db, attrs);
welcomeUser(Email, user);
};
};
The example here demonstrates that the pure function must be honest about its
dependencies and, as such, tell us exactly what it's up to. Just from its signature,
we know that it will use a Db , Email , and attrs which should be telling to
say the least.
We'll learn how to make functions like this pure without merely deferring
evaluation, but the point should be clear that the pure form is much more
informative than its sneaky impure counterpart which is up to who knows what.
When was the last time you copied a method into a new app? One of my favorite
quotes comes from Erlang creator, Joe Armstrong: "The problem with object-
oriented languages is they’ve got all this implicit environment that they carry
around with them. You wanted a banana but what you got was a gorilla holding
the banana... and the entire jungle".
Testable
Next, we come to realize pure functions make testing much easier. We don't have
to mock a "real" payment gateway or setup and assert the state of the world after
each test. We simply give the function input and assert output.
In fact, we find the functional community pioneering new test tools that can blast
our functions with generated input and assert that properties hold on the output.
It's beyond the scope of this book, but I strongly encourage you to search for and
try Quickcheck - a testing tool that is tailored for a purely functional
environment.
Reasonable
Many believe the biggest win when working with pure functions is referential
transparency. A spot of code is referentially transparent when it can be
substituted for its evaluated value without changing the behavior of the program.
Since pure functions always return the same output given the same input, we can
rely on them to always return the same results and thus preserve referential
transparency. Let's see an example.
punch(jobe, michael);
//=> Immutable.Map({name:'Michael', hp:19, team: 'green'})
decrementHP , isSameTeam and punch are all pure and therefore referentially
transparent. We can use a technique called equational reasoning wherein one
substitutes "equals for equals" to reason about code. It's a bit like manually
evaluating the code without taking into account the quirks of programmatic
evaluation. Using referential transparency, let's play with this code a bit.
Since our data is immutable, we can simply replace the teams with their actual
value
We see that it is false in this case so we can remove the entire if branch
And if we inline decrementHP , we see that, in this case, punch becomes a call
to decrement the hp by 1.
This ability to reason about code is terrific for refactoring and understanding
code in general. In fact, we used this technique to refactor our flock of seagulls
program. We used equational reasoning to harness the properties of addition and
multiplication. Indeed, we'll be using these techniques throughout the book.
Parallel Code
Finally, and here's the coup de grâce, we can run any pure function in parallel
since it does not need access to shared memory and it cannot, by definition, have
a race condition due to some side effect.
This is very much possible in a server side js environment with threads as well
as in the browser with web workers though current culture seems to avoid it due
to complexity when dealing with impure functions.
In Summary
We've seen what pure functions are and why we, as functional programmers,
believe they are the cat's evening wear. From this point on, we'll strive to write
all our functions in a pure way. We'll require some extra tools to help us do so,
but in the meantime, we'll try to separate the impure functions from the rest of
the pure code.
Writing programs with pure functions is a tad laborious without some extra tools
in our belt. We have to juggle data by passing arguments all over the place, we're
forbidden to use state, not to mention effects. How does one go about writing
these masochistic programs? Let's acquire a new tool called curry.
Chapter 4: Currying
Chapter 4: Currying
The concept is simple: You can call a function with fewer arguments than it
expects. It returns a function that takes the remaining arguments.
You can choose to call it all at once or simply feed in each argument piecemeal.
increment(2);
// 3
addTen(2);
// 12
Here we've made a function add that takes one argument and returns a
function. By calling it, the returned function remembers the first argument from
then on via the closure. Calling it with both arguments all at once is a bit of a
pain, however, so we can use a special helper function called curry to make
defining and calling functions like this easier.
The pattern I've followed is a simple, but important one. I've strategically
positioned the data we're operating on (String, Array) as the last argument. It will
become clear as to why upon use.
hasSpaces('hello world');
// [ ' ' ]
hasSpaces('spaceless');
// null
censored('Chocolate Rain');
// 'Ch*c*l*t* R**n'
We also have the ability to transform any function that works on single elements
into a function that works on arrays simply by wrapping it with map :
We typically don't define functions that work on arrays, because we can just call
map(getChildren) inline. Same with sort , filter , and other higher order
functions(Higher order function: A function that takes or returns a function).
When we spoke about pure functions, we said they take 1 input to 1 output.
Currying does exactly this: each single argument returns a new function
expecting the remaining arguments. That, old sport, is 1 input to 1 output.
We can make new, useful functions on the fly simply by passing in a few
arguments and as a bonus, we've retained the mathematical function definition
despite multiple arguments.
There are unit tests to run against your exercises as you code them, or you can
just copy-paste into a JavaScript REPL for the early exercises if you wish.
Answers are provided with the code in the repository for this book. Best way to
do the exercises is with an immediate feedback loop.
var _ = require('ramda');
// Exercise 1
//==============
// Refactor to remove all arguments by partially applying the
function.
// Exercise 1a
//==============
// Use map to make a new words fn that works on an array of
strings.
// Exercise 3
//==============
// Use the helper function _keepHighest to refactor max to not
reference any
// arguments.
// LEAVE BE:
var _keepHighest = function(x, y) {
return x >= y ? x : y;
};
// Bonus 1:
// ============
// Wrap array's slice to be functional and curried.
// //[1, 2, 3].slice(0, 2)
var slice = undefined;
// Bonus 2:
// ============
// Use slice to define a function "take" that returns n elements
from the beginning of an array. Make it curried.
// For ['a', 'b', 'c'] with n=2 it should return ['a', 'b'].
var take = undefined;
Chapter 5: Coding by Composing
Functional husbandry
Here's compose :
f and g are functions and x is the value being "piped" through them.
Composition feels like function husbandry. You, breeder of functions, select two
with traits you'd like to combine and mash them together to spawn a brand new
one. Usage is as follows:
In our definition of compose , the g will run before the f , creating a right to
left flow of data. This is much more readable than nesting a bunch of function
calls. Without compose, the above would read:
Instead of inside to outside, we run right to left, which I suppose is a step in the
left direction(boo). Let's look at an example where sequence matters:
reverse will turn the list around while head grabs the initial item. This
results in an effective, albeit inefficient, last function. The sequence of
functions in the composition should be apparent here. We could define a left to
right version, however, we mirror the mathematical version much more closely
as it stands. That's right, composition is straight from the math books. In fact,
perhaps it's time to look at a property that holds for any composition.
// associativity
var associative = compose(f, compose(g, h)) == compose(compose(f,
g), h);
// true
// or
compose(compose(toUpperCase, head), reverse);
Since it doesn't matter how we group our calls to compose , the result will be the
same. That allows us to write a variadic compose and use it as follows:
Applying the associative property gives us this flexibility and peace of mind that
the result will be equivalent. The slightly more complicated variadic definition is
included with the support libraries for this book and is the normal definition
you'll find in libraries like lodash, underscore, and ramda.
// or
var last = compose(head, reverse);
var loudLastUpper = compose(exclaim, toUpperCase, last);
// or
var last = compose(head, reverse);
var angry = compose(exclaim, toUpperCase);
var loudLastUpper = compose(angry, last);
// more variations...
There's no right or wrong answers - we're just plugging our legos together in
whatever way we please. Usually it's best to group things in a reusable way like
last and angry . If familiar with Fowler's "Refactoring", one might recognize
this process as "extract method"...except without all the object state to worry
about.
Pointfree
Pointfree style means never having to say your data. Excuse me. It means
functions that never mention the data upon which they operate. First class
functions, currying, and composition all play well together to create this style.
//pointfree
var snakeCase = compose(replace(/\s+/ig, '_'), toLowerCase);
See how we partially applied replace ? What we're doing is piping our data
through each function of 1 argument. Currying allows us to prepare each
function to just take its data, operate on it, and pass it along. Something else to
notice is how we don't need the data to construct our function in the pointfree
version, whereas in the pointful one, we must have our word available before
anything else.
//pointfree
var initials = compose(join('. '), map(compose(toUpperCase, head)),
split(' '));
Pointfree code can again, help us remove needless names and keep us concise
and generic. Pointfree is a good litmus test for functional code as it lets us know
we've got small functions that take input to output. One can't compose a while
loop, for instance. Be warned, however, pointfree is a double-edged sword and
can sometimes obfuscate intention. Not all functional code is pointfree and that
is O.K. We'll shoot for it where we can and stick with normal functions
otherwise.
Debugging
A common mistake is to compose something like map , a function of two
arguments, without first partially applying it.
latin(['frog', 'eyes']);
// error
latin(['frog', 'eyes']);
// ['EYES!', 'FROG!'])
If you are having trouble debugging a composition, we can use this helpful, but
impure trace function to see what's going on.
// 'the-world-is-a-vampire'
The trace function allows us to view the data at a certain point for debugging
purposes. Languages like haskell and purescript have similar functions for ease
of development.
Composition will be our tool for constructing programs and, as luck would have
it, is backed by a powerful theory that ensures things will work out for us. Let's
examine this theory.
Category theory
Category theory is an abstract branch of mathematics that can formalize
concepts from several different branches such as set theory, type theory, group
theory, logic, and more. It primarily deals with objects, morphisms, and
transformations, which mirrors programming quite closely. Here is a chart of the
same concepts as viewed from each separate theory.
Sorry, I didn't mean to frighten you. I don't expect you to be intimately familiar
with all these concepts. My point is to show you how much duplication we have
so you can see why category theory aims to unify these things.
Category theory is abstract enough to model many things, but let's apply this to
types and functions, which is what we care about at the moment.
A collection of objects The objects will be data types. For instance, String ,
Boolean , Number , Object , etc. We often view data types as sets of all the
possible values. One could look at Boolean as the set of [true, false] and
Number as the set of all possible numeric values. Treating types as sets is useful
because we can use set theory to work with them.
A collection of morphisms The morphisms will be our standard every day pure
functions.
var g = function(x) {
return x.length;
};
var f = function(x) {
return x === 4;
};
var isFourLetterWord = compose(f, g);
var id = function(x) {
return x;
};
You might ask yourself "What in the bloody hell is that useful for?". We'll make
extensive use of this function in the following chapters, but for now think of it as
a function that can stand in for our value - a function masquerading as every day
data.
id must play nicely with compose. Here is a property that always holds for
every unary (unary: a one-argument function) function f:
// identity
compose(id, f) == compose(f, id) == f;
// true
Hey, it's just like the identity property on numbers! If that's not immediately
clear, take some time with it. Understand the futility. We'll be seeing id used
all over the place soon, but for now we see it's a function that acts as a stand in
for a given value. This is quite useful when writing pointfree code.
So there you have it, a category of types and functions. If this is your first
introduction, I imagine you're still a little fuzzy on what a category is and why
it's useful. We will build upon this knowledge throughout the book. As of right
now, in this chapter, on this line, you can at least see it as providing us with some
wisdom regarding composition - namely, the associativity and identity
properties.
What are some other categories, you ask? Well, we can define one for directed
graphs with nodes being objects, edges being morphisms, and composition just
being path concatenation. We can define with Numbers as objects and >= as
morphisms (actually any partial or total order can be a category). There are
heaps of categories, but for the purposes of this book, we'll only concern
ourselves with the one defined above. We have sufficiently skimmed the surface
and must move on.
In Summary
Composition connects our functions together like a series of pipes. Data will
flow through our application as it must - pure functions are input to output after
all, so breaking this chain would disregard output, rendering our software
useless.
We are now at a point where it would serve us well to see some of this in
practice. Let's make an example application.
// Example Data
var CARS = [{
name: 'Ferrari FF',
horsepower: 660,
dollar_value: 700000,
in_stock: true,
}, {
name: 'Spyker C12 Zagato',
horsepower: 650,
dollar_value: 648000,
in_stock: false,
}, {
name: 'Jaguar XKR-S',
horsepower: 550,
dollar_value: 132000,
in_stock: false,
}, {
name: 'Audi R8',
horsepower: 525,
dollar_value: 114200,
in_stock: false,
}, {
name: 'Aston Martin One-77',
horsepower: 750,
dollar_value: 1850000,
in_stock: true,
}, {
name: 'Pagani Huayra',
horsepower: 700,
dollar_value: 1300000,
in_stock: false,
}];
// Exercise 1:
// ============
// Use _.compose() to rewrite the function below. Hint: _.prop() is
curried.
var isLastInStock = function(cars) {
var last_car = _.last(cars);
return _.prop('in_stock', last_car);
};
// Exercise 2:
// ============
// Use _.compose(), _.prop() and _.head() to retrieve the name of
the first car.
var nameOfFirstCar = undefined;
// Exercise 3:
// ============
// Use the helper function _average to refactor averageDollarValue
as a composition.
var _average = function(xs) {
return _.reduce(_.add, 0, xs) / xs.length;
}; // <- leave be
// Bonus 1:
// ============
// Refactor availablePrices with compose.
// Bonus 2:
// ============
// Refactor to pointfree. Hint: you can use _.flip().
Declarative coding
We are going to switch our mindset. From here on out, we'll stop telling the
computer how to do its job and instead write a specification of what we'd like as
a result. I'm sure you'll find it much less stressful than trying to micromanage
everything all the time.
Think of SQL. There is no "first do this, then do that". There is one expression
that specifies what we'd like from the database. We don't decide how to do the
work, it does. When the database is upgraded and the SQL engine optimized, we
don't have to change our query. This is because there are many ways to interpret
our specification and achieve the same result.
For some folks, myself included, it's hard to grasp the concept of declarative
coding at first so let's point out a few examples to get a feel for it.
// imperative
var makes = [];
for (var i = 0; i < cars.length; i++) {
makes.push(cars[i].make);
}
// declarative
var makes = cars.map(function(car) { return car.make; });
The imperative loop must first instantiate the array. The interpreter must evaluate
this statement before moving on. Then it directly iterates through the list of cars,
manually increasing a counter and showing its bits and pieces to us in a vulgar
display of explicit iteration.
The map version is one expression. It does not require any order of evaluation.
There is much freedom here for how the map function iterates and how the
returned array may be assembled. It specifies what, not how. Thus, it wears the
shiny declarative sash.
In addition to being clearer and more concise, the map function may be
optimized at will and our precious application code needn't change.
For those of you who are thinking "Yes, but it's much faster to do the imperative
loop", I suggest you educate yourself on how the JIT optimizes your code. Here's
a terrific video that may shed some light
// imperative
var authenticate = function(form) {
var user = toUser(form);
return logIn(user);
};
// declarative
var authenticate = compose(logIn, toUser);
Though there's nothing necessarily wrong with the imperative version, there is
still an encoded step-by-step evaluation baked in. The compose expression
simply states a fact: Authentication is the composition of toUser and logIn .
Again, this leaves wiggle room for support code changes and results in our
application code being a high level specification.
Because we are not encoding order of evaluation, declarative coding lends itself
to parallel computing. This coupled with pure functions is why FP is a good
option for the parallel future - we don't really need to do anything special to
achieve parallel/concurrent systems.
A flickr of functional programming
We will now build an example application in a declarative, composable way.
We'll still cheat and use side effects for now, but we'll keep them minimal and
separate from our pure codebase. We are going to build a browser widget that
sucks in flickr images and displays them. Let's start by scaffolding the app.
Here's the html:
<!DOCTYPE html>
<html>
<head>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.11/requi
re.min.js"></script>
<script src="flickr.js"></script>
</head>
<body></body>
</html>
requirejs.config({
paths: {
ramda:
'https://cdnjs.cloudflare.com/ajax/libs/ramda/0.13.0/ramda.min',
jquery:
'https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min'
},
});
require([
'ramda',
'jquery',
],
function(_, $) {
var trace = _.curry(function(tag, x) {
console.log(tag, x);
return x;
});
// app goes here
});
We're pulling in ramda instead of lodash or some other utility library. It includes
compose , curry , and more. I've used requirejs, which may seem like overkill,
but we'll be using it throughout the book and consistency is key. Also, I've
started us off with our nice trace function for easy debugging.
Now that that's out of the way, on to the spec. Our app will do 4 things.
There are 2 impure actions mentioned above. Do you see them? Those bits about
getting data from the flickr api and placing it on the screen. Let's define those
first so we can quarantine them.
var Impure = {
getJSON: _.curry(function(callback, url) {
$.getJSON(url, callback);
}),
Here we've simply wrapped jQuery's methods to be curried and we've swapped
the arguments to a more favorable position. I've namespaced them with Impure
There are fancy and overly complex ways of writing url pointfree using
monoids(we'll learn about these later) or combinators. We've chosen to stick with
a readable version and assemble this string in the normal pointful fashion.
Let's write an app function that makes the call and places the contents on the
screen.
app('cats');
This calls our url function, then passes the string to our getJSON function,
which has been partially applied with trace . Loading the app will show the
response from the api call in the console.
We'd like to construct images out of this json. It looks like the srcs are buried in
items then each media 's m property.
Anyhow, to get at these nested properties we can use a nice universal getter
function from ramda called _.prop() . Here's a homegrown version so you can
see what's happening:
Once we gather the items , we must map over them to extract each media url.
This results in a nice array of srcs. Let's hook this up to our app and print them
on the screen.
All we've done is make a new composition that will call our srcs and set the
body html with them. We've replaced the trace call with renderImages now
that we have something to render besides raw json. This will crudely display our
srcs directly in the body.
Our final step is to turn these srcs into bonafide images. In a bigger application,
we'd use a template/dom library like Handlebars or React. For this application
though, we only need an img tag so let's stick with jQuery.
requirejs.config({
paths: {
ramda:
'https://cdnjs.cloudflare.com/ajax/libs/ramda/0.13.0/ramda.min',
jquery:
'https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min',
},
});
require([
'ramda',
'jquery',
],
function(_, $) {
////////////////////////////////////////////
// Utils
var Impure = {
getJSON: _.curry(function(callback, url) {
$.getJSON(url, callback);
}),
////////////////////////////////////////////
app('cats');
});
Now look at that. A beautifully declarative specification of what things are, not
how they come to be. We now view each line as an equation with properties that
hold. We can use these properties to reason about our application and refactor.
A Principled Refactor
There is an optimization available - we map over each item to turn it into a
media url, then we map again over those srcs to turn them into img tags. There is
a law regarding map and composition:
We can use this property to optimize our code. Let's have a principled refactor.
// original code
var mediaUrl = _.compose(_.prop('m'), _.prop('media'));
Let's line up our maps. We can inline the call to srcs in images thanks to
equational reasoning and purity.
Now that we've lined up our map 's we can apply the composition law.
var mediaUrl = _.compose(_.prop('m'), _.prop('media'));
Now the bugger will only loop once while turning each item into an img. Let's
just make it a little more readable by extracting the function out.
When working with pure functions, type signatures have an expressive power to
which the English language cannot hold a candle. These signatures whisper in
your ear the intimate secrets of a function. In a single, compact line, they expose
behaviour and intention. We can derive "free theorems" from them. Types can be
inferred so there's no need for explicit type annotations. They can be tuned to
fine point precision or left general and abstract. They are not only useful for
compile time checks, but also turn out to be the best possible documentation
available. Type signatures thus play an important part in functional programming
- much more than you might first expect.
JavaScript is a dynamic language, but that does not mean we avoid types all
together. We're still working with strings, numbers, booleans, and so on. It's just
that there isn't any language level integration so we hold this information in our
heads. Not to worry, since we're using signatures for documentation, we can use
comments to serve our purpose.
There are type checking tools available for JavaScript such as Flow or the typed
dialect, TypeScript. The aim of this book is to equip one with the tools to write
functional code so we'll stick with the standard type system used across FP
languages.
Tales from the cryptic
From the dusty pages of math books, across the vast sea of white papers,
amongst casual Saturday morning blog posts, down into the source code itself,
we find Hindley-Milner type signatures. The system is quite simple, but warrants
a quick explanation and some practice to fully absorb the little language.
capitalize("smurf");
//=> "Smurf"
Here, capitalize takes a String and returns a String . Never mind the
implementation, it's the type signature we're interested in.
In HM, functions are written as a -> b where a and b are variables for any
type. So the signatures for capitalize can be read as "a function from
String to String ". In other words, it takes a String as its input and returns
a String as its output.
strLength is the same idea as before: we take a String and return you a
Number .
The others might perplex you at first glance. Without fully understanding the
details, you could always just view the last type as the return value. So for
match you can interpret as: It takes a Regex and a String and returns you
[String] . But an interesting thing is going on here that I'd like to take a
moment to explain if I may.
Each argument pops one type off the front of the signature. onHoliday is
match that already has a Regex .
As you can see with the full parenthesis on replace , the extra notation can get
a little noisy and redundant so we simply omit them. We can give all the
arguments at once if we choose so it's easier to just think of it as: replace
// id :: a -> a
var id = function(x) {
return x;
}
The id function takes any old type a and returns something of the same type
a . We're able to use variables in types just like in code. Variable names like a
and b are convention, but they are arbitrary and can be replaced with whatever
name you'd like. If they are the same variable, they have to be the same type.
That's an important rule so let's reiterate: a -> b can be any type a to any
type b , but a -> a means it has to be the same type. For example, id may
be String -> String or Number -> Number , but not String -> Bool .
map similarly uses type variables, but this time we introduce b which may or
may not be the same type as a . We can read it as: map takes a function from
any type a to the same or different type b , then takes an array of a 's and
results in an array of b 's.
Hopefully, you've been overcome by the expressive beauty in this type signature.
It literally tells us what the function does almost word for word. It's given a
function from a to b , an array of a , and it delivers us an array of b . The
only sensible thing for it to do is call the bloody function on each a . Anything
else would be a bold face lie.
Being able to reason about types and their implications is a skill that will take
you far in the functional world. Not only will papers, blogs, docs, etc, become
more digestible, but the signature itself will practically lecture you on its
functionality. It takes practice to become a fluent reader, but if you stick with it,
heaps of information will become available to you sans RTFMing.
Here's a few more just to see if you can decipher them on your own.
reduce is perhaps, the most expressive of all. It's a tricky one, however, so
don't feel inadequate should you struggle with it. For the curious, I'll try to
explain in English though working through the signature on your own is much
more instructive.
Ahem, here goes nothing....looking at the signature, we see the first argument is
a function that expects a b , an a , and produces a b . Where might it get
these a s and b s? Well, the following arguments in the signature are a b and
an array of a s so we can only assume that the b and each of those a s will
be fed in. We also see that the result of the function is a b so the thinking here
is our final incantation of the passed in function will be our output value.
Knowing what reduce does, we can state that the above investigation is accurate.
Narrowing the possibility
Once a type variable is introduced, there emerges a curious property called
parametricity. This property states that a function will act on all types in a
uniform manner. Let's investigate:
Looking at head , we see that it takes [a] to a . Besides the concrete type
array , it has no other information available and, therefore, its functionality is
limited to working on the array alone. What could it possibly do with the
variable a if it knows nothing about it? In other words, a says it cannot be a
specific type, which means it can be any type, which leaves us with a function
that must work uniformly for every conceivable type. This is what parametricity
is all about. Guessing at the implementation, the only reasonable assumptions
are that it takes the first, last, or a random element from that array. The name
head should tip us off.
From the type signature alone, what could reverse possibly be up to? Again, it
cannot do anything specific to a . It cannot change a to a different type or
we'd introduce a b . Can it sort? Well, no, it wouldn't have enough information
to sort every possible type. Can it re-arrange? Yes, I suppose it can do that, but it
has to do so in exactly the same predictable way. Another possibility is that it
may decide to remove or duplicate an element. In any case, the point is, the
possible behaviour is massively narrowed by its polymorphic type.
This narrowing of possibility allows us to use type signature search engines like
Hoogle to find a function we're after. The information packed tightly into a
signature is quite powerful indeed.
Free as in theorem
Besides deducing implementation possibilities, this sort of reasoning gains us
free theorems. What follows are a few random example theorems lifted directly
from Wadler's paper on the subject.
You don't need any code to get these theorems, they follow directly from the
types. The first one says that if we get the head of our array, then run some
function f on it, that is equivalent to, and incidentally, much faster than, if we
first map(f) over every element then take the head of the result.
You might think, well that's just common sense. But last I checked, computers
don't have common sense. Indeed, they must have a formal way to automate
these kind of code optimizations. Maths has a way of formalizing the intuitive,
which is helpful amidst the rigid terrain of computer logic.
function itself. The fruit is low hanging and the possibilities are endless.
Constraints
One last thing to note is that we can constrain types to an interface.
What we see on the left side of our fat arrow here is the statement of a fact: a
Here, we have two constraints: Eq and Show . Those will ensure that we can
check equality of our a s and print the difference if they are not equal.
We'll see more examples of constraints and the idea should take more shape in
later chapters.
In Summary
Hindley-Milner type signatures are ubiquitous in the functional world. Though
they are simple to read and write, it takes time to master the technique of
understanding programs through signatures alone. We will add type signatures to
each line of code from here on out.
Chapter 8: Tupperware
Chapter 8: Tupperware
We've seen how to write programs which pipe data through a series of pure
functions. They are declarative specifications of behaviour. But what about
control flow, error handling, asynchronous actions, state and, dare I say, effects?!
In this chapter, we will discover the foundation upon which all of these helpful
abstractions are built.
First we will create a container. This container must hold any type of value; a
ziplock that holds only tapioca pudding is rarely useful. It will be an object, but
we will not give it properties and methods in the OO sense. No, we will treat it
like a treasure chest - a special box that cradles our valuable data.
Here is our first container. We've thoughtfully named it Container . We will use
Container.of as a constructor which saves us from having to write that awful
new keyword all over the place. There's more to the of function than meets
the eye, but for now, think of it as the proper way to place values into our
container.
Container.of(3);
//=> Container(3)
Container.of('hotdogs');
//=> Container("hotdogs")
Container.of(Container.of({
name: 'yoda',
}));
//=> Container(Container({name: "yoda" }))
If you are using node, you will see {__value: x} even though we've got
ourselves a Container(x) . Chrome will output the type properly, but no matter;
as long as we understand what a Container looks like, we'll be fine. In some
environments you can overwrite the inspect method if you'd like, but we will
not be so thorough. For this book, we will write the conceptual output as if we'd
overwritten inspect as it's much more instructive than {__value: x} for
pedagogical as well as aesthetic reasons.
Container is an object with one property. Lots of containers just hold one
thing, though they aren't limited to one. We've arbitrarily named its property
__value .
The __value cannot be one specific type or our Container would hardly
live up to the name.
Once data goes into the Container it stays there. We could get it out by
using .__value , but that would defeat the purpose.
The reasons we're doing this will become clear as a mason jar, but for now, bear
with me.
My First Functor
Once our value, whatever it may be, is in the container, we'll need a way to run
functions on it.
Why, it's just like Array's famous map , except we have Container a instead
of [a] . And it works essentially the same way:
Container.of(2).map(function(two) {
return two + 2;
});
//=> Container(4)
Container.of("flamethrowers").map(function(s) {
return s.toUpperCase();
});
//=> Container("FLAMETHROWERS")
Container.of("bombs").map(_.concat(' away')).map(_.prop('length'));
//=> Container(10)
We can work with our value without ever having to leave the Container . This
is a remarkable thing. Our value in the Container is handed to the map
function so we can fuss with it and afterward, returned to its Container for
safe keeping. As a result of never leaving the Container , we can continue to
map away, running functions as we please. We can even change the type as we
go along as demonstrated in the latter of the three examples.
Yes, Functor is simply an interface with a contract. We could have just as easily
named it Mappable, but now, where's the fun in that? Functors come from
category theory and we'll look at the maths in detail toward the end of the
chapter, but for now, let's work on intuition and practical uses for this bizarrely
named interface.
What reason could we possibly have for bottling up a value and using map to
get at it? The answer reveals itself if we choose a better question: What do we
gain from asking our container to apply functions for us? Well, abstraction of
function application. When we map a function, we ask the container type to run
it for us. This is a very powerful concept, indeed.
Schrödinger's Maybe
Container is fairly boring. In fact, it is usually called Identity and has about
the same impact as our id function(again there is a mathematical connection
we'll look at when the time is right). However, there are other functors, that is,
container-like types that have a proper map function, which can provide useful
behaviour whilst mapping. Let's define one now.
Maybe.of = function(x) {
return new Maybe(x);
};
Maybe.prototype.isNothing = function() {
return (this.__value === null || this.__value === undefined);
};
Maybe.prototype.map = function(f) {
return this.isNothing() ? Maybe.of(null) :
Maybe.of(f(this.__value));
};
Now, Maybe looks a lot like Container with one minor change: it will first
check to see if it has a value before calling the supplied function. This has the
effect of side stepping those pesky nulls as we map (Note that this
implementation is simplified for teaching).
Maybe.of('Malkovich Malkovich').map(match(/a/ig));
//=> Maybe(['a', 'a'])
Maybe.of(null).map(match(/a/ig));
//=> Maybe(null)
Maybe.of({
name: 'Boris',
}).map(_.prop('age')).map(add(10));
//=> Maybe(null)
Maybe.of({
name: 'Dinah',
age: 14,
}).map(_.prop('age')).map(add(10));
//=> Maybe(24)
Notice our app doesn't explode with errors as we map functions over our null
values. This is because Maybe will take care to check for a value each and
every time it applies a function.
This dot syntax is perfectly fine and functional, but for reasons mentioned in Part
1, we'd like to maintain our pointfree style. As it happens, map is fully
equipped to delegate to whatever functor it receives:
// map :: Functor f => (a -> b) -> f a -> f b
var map = curry(function(f, any_functor_at_all) {
return any_functor_at_all.map(f);
});
This is delightful as we can carry on with composition per usual and map will
work as expected. This is the case with ramda's map as well. We'll use dot
notation when it's instructive and the pointfree version when it's convenient. Did
you notice that? I've sneakily introduced extra notation into our type signature.
The Functor f => tells us that f must be a Functor. Not that difficult, but I
felt I should mention it.
Use cases
In the wild, we'll typically see Maybe used in functions which might fail to
return a result.
streetName({
addresses: [],
});
// Maybe(null)
streetName({
addresses: [{
street: 'Shady Ln.',
number: 4201,
}],
});
// Maybe("Shady Ln.")
safeHead is like our normal _.head , but with added type safety. A curious
thing happens when Maybe is introduced into our code; we are forced to deal
with those sneaky null values. The safeHead function is honest and up front
about its possible failure - there's really nothing to be ashamed of - and so it
returns a Maybe to inform us of this matter. We are more than merely informed,
however, because we are forced to map to get at the value we want since it is
tucked away inside the Maybe object. Essentially, this is a null check
enforced by the safeHead function itself. We can now sleep better at night
knowing a null value won't rear its ugly, decapitated head when we least
expect it. APIs like this will upgrade a flimsy application from paper and tacks
to wood and nails. They will guarantee safer software.
getTwenty({
balance: 200.00,
});
// Maybe("Your balance is $180.00")
getTwenty({
balance: 10.00,
});
// Maybe(null)
withdraw will tip its nose at us and return Maybe(null) if we're short on cash.
This function also communicates its fickleness and leaves us no choice, but to
map everything afterwards. The difference is that the null was intentional
here. Instead of a Maybe(String) , we get the Maybe(null) back to signal
failure and our application effectively halts in its tracks. This is important to
note: if the withdraw fails, then map will sever the rest of our computation
since it doesn't ever run the mapped functions, namely finishTransaction .
This is precisely the intended behaviour as we'd prefer not to update our ledger
or show a new balance if we hadn't successfully withdrawn funds.
Releasing the value
One thing people often miss is that there will always be an end of the line; some
effecting function that sends JSON along, or prints to the screen, or alters our
filesystem, or what have you. We cannot deliver the output with return , we
must run some function or another to send it out into the world. We can phrase it
like a Zen Buddhist koan: "If a program has no observable effect, does it even
run?". Does it run correctly for its own satisfaction? I suspect it merely burns
some cycles and goes back to sleep...
Our application's job is to retrieve, transform, and carry that data along until it's
time to say goodbye and the function which does so may be mapped, thus the
value needn't leave the warm womb of its container. Indeed, a common error is
to try to remove the value from our Maybe one way or another as if the possible
value inside will suddenly materialize and all will be forgiven. We must
understand it may be a branch of code where our value is not around to live up to
its destiny. Our code, much like Schrödinger's cat, is in two states at once and
should maintain that fact until the final function. This gives our code a linear
flow despite the logical branching.
There is, however, an escape hatch. If we would rather return a custom value and
continue on, we can use a little helper called maybe .
getTwenty({
balance: 200.00,
});
// "Your balance is $180.00"
getTwenty({
balance: 10.00,
});
// "You're broke!"
We will now either return a static value (of the same type that
finishTransaction returns) or continue on merrily finishing up the transaction
sans Maybe . With maybe , we are witnessing the equivalent of an if/else
statement whereas with map , the imperative analog would be: if (x !==
The introduction of Maybe can cause some initial discomfort. Users of Swift
and Scala will know what I mean as it's baked right into the core libraries under
the guise of Option(al) . When pushed to deal with null checks all the time
(and there are times we know with absolute certainty the value exists), most
people can't help, but feel it's a tad laborious. However, with time, it will become
second nature and you'll likely appreciate the safety. After all, most of the time it
will prevent cut corners and save our hides.
Writing unsafe software is like taking care to paint each egg with pastels before
hurling it into traffic; like building a retirement home with materials warned
against by three little pigs. It will do us well to put some safety into our
functions and Maybe helps us do just that.
I'd be remiss if I didn't mention that the "real" implementation will split Maybe
into two types: one for something and the other for nothing. This allows us to
obey parametricity in map so values like null and undefined can still be
mapped over and the universal qualification of the value in a functor will be
respected. You'll often see types like Some(x) / None or Just(x) / Nothing
It may come as a shock, but throw/catch is not very pure. When an error is
thrown, instead of returning an output value, we sound the alarms! The function
attacks, spewing thousands of 0's and 1's like shields & spears in an electric
battle against our intruding input. With our new friend Either , we can do
better than to declare war on input, we can respond with a polite message. Let's
take a look:
Left.of = function(x) {
return new Left(x);
};
Left.prototype.map = function(f) {
return this;
};
Right.of = function(x) {
return new Right(x);
};
Right.prototype.map = function(f) {
return Right.of(f(this.__value));
}
Left and Right are two subclasses of an abstract type we call Either . I've
skipped the ceremony of creating the Either superclass as we won't ever use
it, but it's good to be aware. Now then, there's nothing new here besides the two
types. Let's see how they act:
Right.of('rain').map(function(str) {
return 'b' + str;
});
// Right('brain')
Left.of('rain').map(function(str) {
return 'b' + str;
});
// Left('rain')
Right.of({
host: 'localhost',
port: 80,
}).map(_.prop('host'));
// Right('localhost')
Left.of('rolls eyes...').map(_.prop('host'));
// Left('rolls eyes...')
Left is the teenagery sort and ignores our request to map over it. Right will
work just like Container (a.k.a Identity). The power comes from the ability to
embed an error message within the Left .
Suppose we have a function that might not succeed. How about we calculate an
age from a birth date. We could use Maybe(null) to signal failure and branch
our program, however, that doesn't tell us much. Perhaps, we'd like to know why
it failed. Let's write this using Either .
getAge(moment(), {
birthdate: '2005-12-12',
});
// Right(9)
getAge(moment(), {
birthdate: 'July 4, 2001',
});
// Left('Birth date could not be parsed')
Now, just like Maybe(null) , we are short circuiting our app when we return a
Left . The difference, is now we have a clue as to why our program has
derailed. Something to notice is that we return Either(String, Number) , which
holds a String as its left value and a Number as its Right . This type
signature is a bit informal as we haven't taken the time to define an actual
Either superclass, however, we learn a lot from the type. It informs us that
we're either getting an error message or the age back.
zoltar({
birthdate: '2005-12-12',
});
// 'If you survive, you will be 10'
// Right(undefined)
zoltar({
birthdate: 'balloons!',
});
// Left('Birth date could not be parsed')
When the birthdate is valid, the program outputs its mystical fortune to the
screen for us to behold. Otherwise, we are handed a Left with the error
message plain as day though still tucked away in its container. That acts just as if
we'd thrown an error, but in a calm, mild manner fashion as opposed to losing its
temper and screaming like a child when something goes wrong.
In this example, we are logically branching our control flow depending on the
validity of the birth date, yet it reads as one linear motion from right to left rather
than climbing through the curly braces of a conditional statement. Usually, we'd
move the console.log out of our zoltar function and map it at the time of
calling, but it's helpful to see how the Right branch differs. We use _ in the
right branch's type signature to indicate it's a value that should be ignored(In
some browsers you have to use console.log.bind(console) to use it first
class).
I'd like to take this opportunity to point out something you may have missed:
fortune , despite its use with Either in this example, is completely ignorant
of any functors milling about. This was also the case with finishTransaction
Either is great for casual errors like validation as well as more serious, stop
the show errors like missing files or broken sockets. Try replacing some of the
Maybe examples with Either to give better feedback.
Now, I can't help, but feel I've done Either a disservice by introducing it as
merely a container for error messages. It captures logical disjunction (a.k.a || )
in a type. It also encodes the idea of a Coproduct from category theory, which
won't be touched on in this book, but is well worth reading up on as there's
properties to be exploited. It is the canonical sum type (or disjoint union of sets)
because its amount of possible inhabitants is the sum of the two contained types
(I know that's a bit hand wavy so here's a great article). There are many things
Either can be, but as a functor, it is used for its error handling.
Just like with Maybe , we have little either , which behaves similarly, but
takes two functions instead of one and a static value. Each function should return
the same type:
zoltar({
birthdate: '2005-12-12',
});
// "If you survive, you will be 10"
// undefined
zoltar({
birthdate: 'balloons!',
});
// "Birth date could not be parsed"
// undefined
Finally, a use for that mysterious id function. It simply parrots back the value
in the Left to pass the error message to console.log . We've made our
fortune telling app more robust by enforcing error handling from within
getAge . We either slap the user with a hard truth like a high five from a palm
reader or we carry on with our process. And with that, we're ready to move on to
an entirely different type of functor.
Old McDonald had Effects...
In our chapter about purity we saw a peculiar example of a pure function. This
function contained a side-effect, but we dubbed it pure by wrapping its action in
another function. Here's another example of this:
Except, this isn't particularly useful now is it. Like a collectible action figure in
its original packaging, we can't actually play with it. If only there were a way to
reach inside of the container and get at its contents... Enter IO .
var IO = function(f) {
this.__value = f;
};
IO.of = function(x) {
return new IO(function() {
return x;
});
};
IO.prototype.map = function(f) {
return new IO(_.compose(f, this.__value));
};
io_window.map(function(win) {
return win.innerWidth;
});
// IO(1430)
io_window.map(_.prop('location')).map(_.prop('href')).map(_.split('
/'));
// IO(["http:", "", "localhost:8000", "blog", "posts"])
$('#myDiv').map(head).map(function(div) {
return div.innerHTML;
});
// IO('I am some inner html')
Now, we've caged the beast, but we'll still have to set it free at some point.
Mapping over our IO has built up a mighty impure computation and running it
is surely going to disturb the peace. So where and when can we pull the trigger?
Is it even possible to run our IO and still wear white at our wedding? The
answer is yes, if we put the onus on the calling code. Our pure code, despite the
nefarious plotting and scheming, maintains its innocence and it's the caller who
gets burdened with the responsibility of actually running the effects. Let's see an
example to make this concrete.
// url :: IO String
var url = new IO(function() {
return window.location.href;
});
Our library keeps its hands clean by wrapping url in an IO and passing the
buck to the caller. You might have also noticed that we have stacked our
containers; it's perfectly reasonable to have a IO(Maybe([x])) , which is three
functors deep( Array is most definitely a mappable container type) and
exceptionally expressive.
var IO = function(f) {
this.unsafePerformIO = f;
};
IO.prototype.map = function(f) {
return new IO(_.compose(f, this.unsafePerformIO));
};
There, much better. Now our calling code becomes
findParam("searchTerm").unsafePerformIO() , which is clear as day to users
(and readers) of the application.
The internals are a bit too complicated to spill out all over the page here so we
will use Data.Task (previously Data.Future ) from Quildreen Motta's
fantastic Folktale. Behold some example usage:
var fs = require('fs');
readFile('metamorphosis').map(split('\n')).map(head);
// Task("One morning, as Gregor Samsa was waking up from anxious
dreams, he discovered that
// in bed he had been changed into a monstrous verminous bug.")
// jQuery getJSON example:
//========================
getJSON('/video', {
id: 10,
}).map(_.prop('title'));
// Task("Family Matters ep 15")
The functions I'm calling reject and result are our error and success
callbacks, respectively. As you can see, we simply map over the Task to work
on the future value as if it was right there in our grasp. By now map should be
old hat.
If you're familiar with promises, you might recognize the function map as
then with Task playing the role of our promise. Don't fret if you aren't
familiar with promises, we won't be using them anyhow because they are not
pure, but the analogy holds nonetheless.
Like IO , Task will patiently wait for us to give it the green light before
running. In fact, because it waits for our command, IO is effectively subsumed
by Task for all things asynchronous; readFile and getJSON don't require
an extra IO container to be pure. What's more, Task works in a similar
fashion when we map over it: we're placing instructions for the future like a
chore chart in a time capsule - an act of sophisticated technological
procrastination.
To run our Task , we must call the method fork . This works like
unsafePerformIO , but as the name suggests, it will fork our process and
evaluation continues on without blocking our thread. This can be implemented in
numerous ways with threads and such, but here it acts as a normal async call
would and the big wheel of the event loop keeps on turning. Let's look at fork :
// Pure application
//=====================
// blogTemplate :: String
$('#spinner').show();
Upon calling fork , the Task hurries off to find some posts and render the
page. Meanwhile, we show a spinner since fork does not wait for a response.
Finally, we will either display an error or render the page onto the screen
depending if the getJSON call succeeded or not.
Take a moment to consider how linear the control flow is here. We just read
bottom to top, right to left even though the program will actually jump around a
bit during execution. This makes reading and reasoning about our application
simpler than having to bounce between callbacks and error handling blocks.
Goodness, would you look at that, Task has also swallowed up Either ! It
must do so in order to handle futuristic failures since our normal control flow
does not apply in the async world. This is all well and good as it provides
sufficient and pure error handling out of the box.
Even with Task , our IO and Either functors are not out of a job. Bear with
me on a quick example that leans toward the more complex and hypothetical
side, but is useful for illustrative purposes.
// Pure application
//=====================
In this example, we still make use of Either and IO from within the success
branch of readFile . Task takes care of the impurities of reading a file
asynchronously, but we still deal with validating the config with Either and
wrangling the db connection with IO . So you see, we're still in business for all
things synchronous.
// identity
map(id) === id;
// composition
compose(map(f), map(g)) === map(compose(f, g));
The identity law is simple, but important. These laws are runnable bits of code so
we can try them on our own functors to validate their legitimacy.
idLaw1(Container.of(2));
//=> Container(2)
idLaw2(Container.of(2));
//=> Container(2)
compLaw1(Container.of('Goodbye'));
//=> Container(' world cruelGoodbye')
compLaw2(Container.of('Goodbye'));
//=> Container(' world cruelGoodbye')
In category theory, functors take the objects and morphisms of a category and
map them to a different category. By definition, this new category must have an
identity and the ability to compose morphisms, but we needn't check because the
aforementioned laws ensure these are preserved.
Perhaps our definition of a category is still a bit fuzzy. You can think of a
category as a network of objects with morphisms that connect them. So a functor
would map the one category to the other without breaking the network. If an
object a is in our source category C , when we map it to category D with
functor F , we refer to that object as F a (If you put it together what does that
spell?!). Perhaps, it's better to look at a diagram:
For instance, Maybe maps our category of types and functions to a category
where each object may not exist and each morphism has a null check. We
accomplish this in code by surrounding each function with map and each type
with our functor. We know that each of our normal types and functions will
continue to compose in this new world. Technically, each functor in our code
maps to a sub category of types and functions which makes all functors a
particular brand called endofunctors, but for our purposes, we'll think of it as a
different category.
We can also visualize the mapping of a morphism and its corresponding objects
with this diagram:
bottomRoute('hi');
// Maybe('ih')
Or visually:
We can instantly see and refactor code based on properties held by all functors.
map(map(map(toUpperCase)), nested);
// Task([Right('PILLOWS'), Left('no sleep for you')])
What we have here with nested is a future array of elements that might be
errors. We map to peel back each layer and run our function on the elements.
We see no callbacks, if/else's, or for loops; just an explicit context. We do,
however, have to map(map(map(f))) . We can instead compose functors. You
heard me correctly:
Compose.prototype.map = function(f) {
return new Compose(map(map(f), this.getCompose));
};
ctmd.getCompose;
// Task(Maybe('Rock over London'))
What about calling a function with multiple functor arguments? How about
working with an order sequence of impure or async actions? We haven't yet
acquired the full tool set for working in this boxed up world. Next, we'll cut right
to the chase and look at monads.
// Exercise 1
// ==========
// Use _.add(x,y) and _.map(f,x) to make a function that increments
a value
// inside a functor.
// Exercise 2
// ==========
// Use _.head to get the first element of the list.
var xs = Identity.of(['do', 'ray', 'me', 'fa', 'so', 'la', 'ti',
'do']);
// Exercise 3
// ==========
// Use safeProp and _.head to find the first initial of the user.
var safeProp = _.curry(function(x, o) {
return Maybe.of(o[x]);
});
var user = {
id: 2,
name: 'Albert',
};
// Exercise 4
// ==========
// Use Maybe to rewrite ex4 without an if statement.
// Exercise 5
// ==========
// Write a function that will getPost then toUpperCase the post's
title.
// Exercise 6
// ==========
// Write a function that uses checkActive() and showWelcome() to
grant access
// or return the error.
// Exercise 7
// ==========
// Write a validation function that checks for a length > 3. It
should return
// Right(x) if it is greater than 3 and Left("You need > 3")
otherwise.
// Exercise 8
// ==========
// Use ex7 above and Either as a functor to save the user if they
are valid or
// return the error message string. Remember either's two arguments
must return
// the same type.
What's important here is the ability to drop any value in our type and start
mapping away.
IO.of('tetris').map(concat(' master'));
// IO('tetris master')
Maybe.of(1336).map(add(1));
// Maybe(1337)
Task.of({
id: 2,
}).map(_.prop('id'));
// Task(2)
If you recall, IO and Task 's constructors expect a function as their argument,
but Maybe and Either do not. The motivation for this interface is a common,
consistent way to place a value into our functor without the complexities and
specific demands of constructors. The term "default minimal context" lacks
precision, yet captures the idea well: we'd like to lift any value in our type and
map away per usual with the expected behaviour of whichever functor.
One important correction I must make at this point, pun intended, is that
Left.of doesn't make any sense. Each functor must have one way to place a
value inside it and with Either , that's new Right(x) . We define of using
Right because if our type can map , it should map . Looking at the examples
above, we should have an intuition about how of will usually work and Left
You may have heard of functions such as pure , point , unit , and return .
These are various monikers for our of method, international function of
mystery. of will become important when we start using monads because, as we
will see, it's our responsibility to place values back into the type manually.
To avoid the new keyword, there are several standard JavaScript tricks or
libraries so let's use them and use of like a responsible adult from here on out.
I recommend using functor instances from folktale , ramda or fantasy-
land as they provide the correct of method as well as nice constructors that
don't rely on new .
Mixing Metaphors
You see, in addition to space burritos (if you've heard the rumors), monads are
like onions. Allow me to demonstrate with a common situation:
// Support
// ===========================
var fs = require('fs');
// Example
// ===========================
// cat :: String -> IO (IO String)
var cat = compose(map(print), readFile);
cat('.git/config');
// IO(IO('[core]\nrepositoryformatversion = 0\n'))
introduced a second IO during our map . To continue working with our string,
we must map(map(f)) and to observe the effect, we must
unsafePerformIO().unsafePerformIO() .
catFirstChar(".git/config");
// IO(IO("["))
While it is nice to see that we have two effects packaged up and ready to go in
our application, it feels a bit like working in two hazmat suits and we end up
with an uncomfortably awkward API. Let's look at another situation:
// safeProp :: Key -> {Key: a} -> Maybe a
var safeProp = curry(function(x, obj) {
return new Maybe(obj[x]);
});
firstAddressStreet({
addresses: [{
street: {
name: 'Mulburry',
number: 8402,
},
postcode: 'WC2N',
}],
});
// Maybe(Maybe(Maybe({name: 'Mulburry', number: 8402})))
Again, we see this nested functor situation where it's neat to see there are three
possible failures in our function, but it's a little presumptuous to expect a caller
to map three times to get at the value - we'd only just met. This pattern will
arise time and time again and it is the primary situation where we'll need to shine
the mighty monad symbol into the night sky.
I said monads are like onions because tears well up as we peel back layer of the
nested functor with map to get at the inner value. We can dry our eyes, take a
deep breath, and use a method called join .
mmo.join();
// Maybe('nunchucks')
ioio.join();
// IO('pizza')
ttt.join();
// Task(Task('sewers'))
If we have two layers of the same type, we can smash them together with join .
This ability to join together, this functor matrimony, is what makes a monad a
monad. Let's inch toward the full definition with something a little more
accurate:
Any functor which defines a join method, has an of method, and obeys a
few laws is a monad. Defining join is not too difficult so let's do so for
Maybe :
Maybe.prototype.join = function() {
return this.isNothing() ? Maybe.of(null) : this.__value;
}
Now that we have a join method, let's sprinkle some magic monad dust over
the firstAddressStreet example and see it in action:
firstAddressStreet({
addresses: [{
street: {
name: 'Mulburry',
number: 8402,
},
postcode: 'WC2N',
}],
});
// Maybe({name: 'Mulburry', number: 8402})
We added join wherever we encountered the nested Maybe 's to keep them
from getting out of hand. Let's do the same with IO to give us a feel for that.
IO.prototype.join = function() {
var thiz = this;
return new IO(function() {
return thiz.unsafePerformIO().unsafePerformIO();
});
};
We simply bundle running the two layers of IO sequentially: outer then inner.
Mind you, we have not thrown out purity, but merely repackaged the excessive
two layers of shrink wrap into one easier-to-open package.
// log :: a -> IO a
var log = function(x) {
return new IO(function() {
console.log(x);
return x;
});
};
applyPreferences('preferences').unsafePerformIO();
// Object {backgroundColor: "green"}
// <div style="background-color: 'green'"/>
You might have noticed a pattern. We often end up calling join right after a
map . Let's abstract this into a function called chain .
We'll just bundle up this map/join combo into a single function. If you've read
about monads previously, you might have seen chain called >>=
(pronounced bind) or flatMap which are all aliases for the same concept. I
personally think flatMap is the most accurate name, but we'll stick with
chain as it's the widely accepted name in JS. Let's refactor the two examples
above with chain :
// map/join
var firstAddressStreet = compose(
join, map(safeProp('street')), join, map(safeHead),
safeProp('addresses')
);
// chain
var firstAddressStreet = compose(
chain(safeProp('street')), chain(safeHead), safeProp('addresses')
);
// map/join
var applyPreferences = compose(
join, map(setStyle('#main')), join, map(log), map(JSON.parse),
getItem
);
// chain
var applyPreferences = compose(
chain(setStyle('#main')), chain(log), map(JSON.parse), getItem
);
I swapped out any map/join with our new chain function to tidy things up a
bit. Cleanliness is nice and all, but there's more to chain than meets the eye -
it's more of tornado than a vacuum. Because chain effortlessly nests effects,
we can capture both sequence and variable assignment in a purely functional
way.
// getJSON :: Url -> Params -> Task JSON
// querySelector :: Selector -> IO DOM
getJSON('/authenticate', {
username: 'stale',
password: 'crackers',
})
.chain(function(user) {
return getJSON('/friends', {
user_id: user.id,
});
});
// Task([{name: 'Seimith', id: 14}, {name: 'Ric', id: 39}]);
querySelector('input.username').chain(function(uname) {
return querySelector('input.email').chain(function(email) {
return IO.of(
'Welcome ' + uname.value + ' ' + 'prepare for spam at ' +
email.value
);
});
});
// IO('Welcome Olivia prepare for spam at
[email protected]');
Maybe.of(3).chain(function(three) {
return Maybe.of(2).map(add(three));
});
// Maybe(5);
Maybe.of(null).chain(safeProp('address')).chain(safeProp('street'))
;
// Maybe(null);
We could have written these examples with compose , but we'd need a few
helper functions and this style lends itself to explicit variable assignment via
closure anyhow. Instead we're using the infix version of chain which,
incidentally, can be derived from map and join for any type automatically:
t.prototype.chain = function(f) { return this.map(f).join(); } . We can
also define chain manually if we'd like a false sense of performance, though
we must take care to maintain the correct functionality - that is, it must equal
map followed by join . An interesting fact is that we can derive map for free
if we've created chain simply by bottling the value back up when we're
finished with of . With chain , we can also define join as chain(id) . It
may feel like playing Texas Hold em' with a rhinestone magician in that I'm just
pulling things out of my behind, but, as with most mathematics, all of these
principled constructs are interrelated. Lots of these derivations are mentioned in
the fantasyland repo, which is the official specification for algebraic data types
in JavaScript.
Anyways, let's get to the examples above. In the first example, we see two
Task 's chained in a sequence of asynchronous actions - first it retrieves the
user , then it finds the friends with that user's id. We use chain to avoid a
Task(Task([Friend])) situation.
querySelector('input.username').chain(function(uname) {
return querySelector('input.email').map(function(email) {
return 'Welcome ' + uname.value + ' prepare for spam at ' +
email.value;
});
});
// IO('Welcome Olivia prepare for spam at
[email protected]');
Finally, we have two examples using Maybe . Since chain is mapping under
the hood, if any value is null , we stop the computation dead in its tracks.
Don't worry if these examples are hard to grasp at first. Play with them. Poke
them with a stick. Smash them to bits and reassemble. Remember to map when
returning a "normal" value and chain when we're returning another functor.
As a reminder, this does not work with two different nested types. Functor
composition and later, monad transformers, can help us in that situation.
Power trip
Container style programming can be confusing at times. We sometimes find
ourselves struggling to understand how many containers deep a value is or if we
need map or chain (soon we'll see more container methods). We can greatly
improve debugging with tricks like implementing inspect and we'll learn how
to create a "stack" that can handle whatever effects we throw at it, but there are
times when we question if it's worth the hassle.
I'd like to swing the fiery monadic sword for a moment to exhibit the power of
programming this way.
Here, we are branching our code several times. Looking at the type signatures I
can see that we protect against 3 errors - readFile uses Either to validate
the input (perhaps ensuring the filename is present), readFile may error when
accessing the file as expressed in the first type parameter of Task , and the
upload may fail for whatever reason which is expressed by the Error in
httpPost . We casually pull off two nested, sequential asynchronous actions
with chain .
All of this is achieved in one linear left to right flow. This is all pure and
declarative. It holds equational reasoning and reliable properties. We aren't
forced to add needless and confusing variable names. Our upload function is
written against generic interfaces and not specific one-off APIs. It's one bloody
line for goodness sake.
For contrast, let's look at the standard imperative way to pull this off:
Well isn't that the devil's arithmetic. We're pinballed through a volatile maze of
madness. Imagine if it were a typical app that also mutated variables along the
way! We'd be in the tar pit indeed.
Theory
The first law we'll look at is associativity, but perhaps not in the way you're used
to it.
// associativity
compose(join, map(join)) == compose(join, join);
These laws get at the nested nature of monads so associativity focuses on joining
the inner or outer types first to achieve the same result. A picture might be more
instructive:
Starting with the top left moving downward, we can join the outer two M 's
of M(M(M a)) first then cruise over to our desired M a with another join .
Alternatively, we can pop the hood and flatten the inner two M 's with
map(join) . We end up with the same M a regardless of if we join the inner or
outer M 's first and that's what associativity is all about. It's worth noting that
map(join) != join . The intermediate steps can vary in value, but the end
result of the last join will be the same.
The second law is similar:
It states that, for any monad M , of and join amounts to id . We can also
map(of) and attack it from the inside out. We call this "triangle identity"
because it makes such a shape when visualized:
If we start at the top left heading right, we can see that of does indeed drop our
M a in another M container. Then if we move downward and join it, we get
the same as if we just called id in the first place. Moving right to left, we see
that if we sneak under the covers with map and call of of the plain a , we'll
still end up with M (M a) and join ing will bring us back to square one.
I should mention that I've just written of , however, it must be the specific
M.of for whatever monad we're using.
Now, I've seen these laws, identity and associativity, somewhere before... Hold
on, I'm thinking...Yes of course! They are the laws for a category. But that would
mean we need a composition function to complete the definition. Behold:
var mcompose = function(f, g) {
return compose(chain(f), g);
};
// left identity
mcompose(M, f) == f;
// right identity
mcompose(f, M) == f;
// associativity
mcompose(mcompose(f, g), h) === mcompose(f, mcompose(g, h));
They are the category laws after all. Monads form a category called the "Kleisli
category" where all objects are monads and morphisms are chained functions. I
don't mean to taunt you with bits and bobs of category theory without much
explanation of how the jigsaw fits together. The intention is to scratch the
surface enough to show the relevance and spark some interest while focusing on
the practical properties we can use each day.
In Summary
Monads let us drill downward into nested computations. We can assign
variables, run sequential effects, perform asynchronous tasks, all without laying
one brick in a pyramid of doom. They come to the rescue when a value finds
itself jailed in multiple layers of the same type. With the help of the trusty
sidekick "pointed", monads are able to lend us an unboxed value and know we'll
be able to place it back in when we're done.
Yes, monads are very powerful, yet we still find ourselves needing some extra
container functions. For instance, what if we wanted to run a list of api calls at
once, then gather the results? We can accomplish this task with monads, but we'd
have to wait for each one to finish before calling the next. What about
combining several validations? We'd like to continue validating to gather the list
of errors, but monads would stop the show after the first Left entered the
picture.
In the next chapter, we'll see how applicative functors fit into the container world
and why we prefer them to monads in many cases.
// Exercise 2
// ==========
// Use getFile to get the filename, remove the directory so it's
just the file,
// then purely log it.
// Exercise 3
// ==========
// Use getPost() then pass the post's id to getComments().
//
var getPost = function(i) {
return new Task(function(rej, res) {
setTimeout(function() {
res({
id: i,
title: 'Love them tasks',
});
}, 300);
});
};
// Exercise 4
// ==========
// Use validateEmail, addToMailingList, and emailBlast to implement
ex4's type
// signature.
function emailBlast(list) {
return new IO(function() {
return 'emailed: ' + list.join(',');
});
}
Applying Applicatives
The name applicative functor is pleasantly descriptive given its functional
origins. Functional programmers are notorious for coming up with names like
mappend or liftA4 , which seem perfectly natural when viewed in the math
lab, but hold the clarity of an indecisive Darth Vader at the drive thru in any
other context.
Anyhow, the name should spill the beans on what this interface gives us: the
ability to apply functors to each other.
Now, why would a normal, rational person such as yourself want such a thing?
What does it even mean to apply one functor to another?
To answer these questions, we'll start with a situation you may have already
encountered in your functional travels. Let's say, hypothetically, that we have
two functors (of the same type) and we'd like to call a function with both of their
values as arguments. Something simple like adding the values of two
Container s.
Now, it just so happens that we already have the tools to accomplish this task.
We can chain and then map the partially applied add(2) like so:
Container.of(2).chain(function(two) {
return Container.of(3).map(add(two));
});
The issue here is that we are stuck in the sequential world of monads wherein
nothing may be evaluated until the previous monad has finished its business. We
have ourselves two strong, independent values and I should think it unnecessary
to delay the creation of Container(3) merely to satisfy the monad's sequential
demands.
ap is a function that can apply the function contents of one functor to the value
contents of another. Say that five times fast.
Container.of(add(2)).ap(Container.of(3));
// Container(5)
There we are, nice and neat. Good news for Container(3) as it's been set free
from the jail of the nested monadic function. It's worth mentioning again that
add , in this case, gets partially applied during the first map so this only works
when add is curried.
We can define ap like so:
Container.prototype.ap = function(other_container) {
return other_container.map(this.__value);
}
Note the dependence on pointed. The pointed interface is crucial here as we'll
see throughout the following examples.
Now, I sense your skepticism (or perhaps confusion and horror), but keep an
open mind; this ap character will prove useful. Before we get into it, let's
explore a nice property.
F.of(x).map(f) == F.of(f).ap(F.of(x))
Maybe.of(add).ap(Maybe.of(2)).ap(Maybe.of(3));
// Maybe(5)
Task.of(add).ap(Task.of(2)).ap(Task.of(3));
// Task(5)
One might even recognise the vague shape of a normal function call if viewed
mid squint. We'll look at the pointfree version later in the chapter, but for now,
this is the preferred way to write such code. Using of , each value gets
transported to the magical land of containers, this parallel universe where each
application can be async or null or what have you and ap will apply functions
within this fantastical place. It's like building a ship in a bottle.
Did you see there? We used Task in our example. This is a prime situation
where applicative functors pull their weight. Let's look at a more in-depth
example.
Coordination Motivation
Say we're building a travel site and we'd like to retrieve both a list of tourist
destinations and local events. Each of these are separate, stand-alone api calls.
Task.of(renderPage).ap(Http.get('/destinations')).ap(Http.get('/eve
nts'))
// Task("<div>some page with dest and events</div>")
Both Http calls will happen instantly and renderPage will be called when
both are resolved. Contrast this with the monadic version where one Task must
finish before the next fires off. Since we don't need the destinations to retrieve
events, we are free from sequential evaluation.
Again, because we're using partial application to achieve this result, we must
ensure renderPage is curried or it will not wait for both Tasks to finish.
Incidentally, if you've ever had to do such a thing manually, you'll appreciate the
astonishing simplicity of this interface. This is the kind of beautiful code that
takes us one step closer to the singularity.
// Helpers:
// ==============
// $ :: String -> IO DOM
var $ = function(selector) {
return new IO(function() { return
document.querySelector(selector)});
}
// Example:
// ===============
// signIn :: String -> String -> Bool -> User
var signIn = curry(function(username, password, remember_me) { /*
signing in */ })
IO.of(signIn).ap(getVal('#email')).ap(getVal('#password')).ap(IO.of(
false));
// IO({id: 3, email: "[email protected]"})
//liftA4, etc
liftA2 is a strange name. It sounds like one of the finicky freight elevators in a
rundown factory or a vanity plate for a cheap limo company. Once enlightened,
however, it's self explanatory: lift these pieces into the applicative functor world.
When I first saw this 2-3-4 nonsense it struck me as ugly and unnecessary. After
all, we can check the arity of functions in JavaScript and build this up
dynamically. However, it is often useful to partially apply liftA(N) itself, so it
cannot vary in argument length.
Either.of(createUser).ap(checkEmail(user)).ap(checkName(user));
// Left("invalid email")
-- haskell
add <$> Right 2 <*> Right 3
// JavaScript
map(add, Right.of(2)).ap(Right.of(3))
It's helpful to know that <$> is map (aka fmap ) and <*> is just ap . This
allows for a more natural function application style and can help remove some
parenthesis.
Free can openers
Monads are at the top of the food chain, so to speak, so if we have chain , we
get functor and applicative for free:
If we can define a monad, we can define both the applicative and functor
interfaces. This is quite remarkable as we get all of these can openers for free.
We can even examine a type and automate this process.
It should be pointed out that part of ap 's appeal is the ability to run things
concurrently so defining it via chain is missing out on that optimization.
Despite that, it's good to have an immediate working interface while one works
out the best possible implementation.
Why not just use monads and be done with it, you ask? It's good practice to work
with the level of power you need, no more, no less. This keeps cognitive load to
a minimum by ruling out possible functionality. For this reason, it's good to
favor applicatives over monads.
Monads have the unique ability to sequence computation, assign variables, and
halt further execution all thanks to the downward nesting structure. When one
sees applicatives in use, they needn't concern themselves with any of that
business.
To demonstrate:
Identity
// identity
A.of(id).ap(v) == v
Right, so applying id all from within a functor shouldn't alter the value in v .
For example:
The beauty in using these laws is that, like a militant kindergarten gym coach,
they force all of our interfaces to play well together.
Homomorphism
// homomorphism
A.of(f).ap(A.of(x)) == A.of(f(x))
We're really just stuffing our normal functions and values into a container and
running the computation in there so it should come as no surprise that we will
end up with the same result if we apply the whole thing inside the container (left
side of the equation) or apply it outside, then place it in there (right side).
A quick example:
Either.of(_.toUpper).ap(Either.of("oreos")) ==
Either.of(_.toUpper("oreos"))
Interchange
The interchange law states that it doesn't matter if we choose to lift our function
into the left or right side of ap .
// interchange
v.ap(A.of(x)) == A.of(function(f) { return f(x) }).ap(v)
Here is an example:
var v = Task.of(_.reverse);
var x = 'Sparklehorse';
Composition
And finally composition which is just a way to check that our standard function
composition holds when applying inside of containers.
// composition
A.of(compose).ap(u).ap(v).ap(w) == u.ap(v.ap(w));
var u = IO.of(_.toUpper);
var v = IO.of(_.concat("& beyond"));
var w = IO.of("blood bath ");
IO.of(_.compose).ap(u).ap(v).ap(w) == u.ap(v.ap(w))
In Summary
A good use case for applicatives is when one has multiple functor arguments.
They give us the ability to apply functions to arguments all within the functor
world. Though we could already do so with monads, we should prefer
applicative functors when we aren't in need of monadic specific functionality.
We're almost finished with container APIs. We've learned how to map , chain ,
and now ap functions. In the next chapter, we'll learn how to work better with
multiple functors and disassemble them in a principled way.
// Exercise 1
// ==========
// Write a function that adds two possibly null numbers together
using Maybe and ap().
// Exercise 2
// ==========
// Now write a function that takes 2 Maybe's and adds them. Use
liftA2 instead of ap().
// Exercise 3
// ==========
// Run both getPost(n) and getComments(n) then render the page with
both. (The n arg is arbitrary.)
var makeComments = _.reduce(function(acc, c) { return acc+"
<li>"+c+"</li>" }, "");
var render = _.curry(function(p, cs) { return "<div>"+p.title+"
</div>"+makeComments(cs); });
// Exercise 4
// ==========
// Write an IO that gets both player1 and player2 from the cache
and starts the game.
localStorage.player1 = "toby";
localStorage.player2 = "sally";
// ex4 :: IO String
var ex4 = undefined;
// TEST HELPERS
// =====================
function getPost(i) {
return new Task(function (rej, res) {
setTimeout(function () { res({ id: i, title: 'Love them
futures' }); }, 300);
});
}
function getComments(i) {
return new Task(function (rej, res) {
setTimeout(function () {
res(["This book should be illegal", "Monads are like space
burritos"]);
}, 300);
});
}