The road less traveled (feat. Sanctuary and Ninja Turtles) —episode 1.

In the great tradition of Michelangelo, let’s start with pizza.

Say there’s four turtles of the mutant variety, and a single pie. These turtles are teenagers- and hungry! Yet, they’re also ninjas and have been trained with discipline to share. Leonardo, the leader of the group, wants to do things fairly, so he whips together a simple program:

Bad pizza code… like, anchovies bad!

Donatello, being the whiz that he is, pats Leo on the shell and gently breaks the news to him, “Brother… that’s a mess.” Donny’s got a whole bag of tricks he starts teaching Leo: throwing exceptions, returning once, returning early… some inheritance and overrides... abstract classes… state machines… many smart ways to make the code more readable. And it does work, until a wise rat busts the bubble.

“Donatello is correct”, Splinter says. “However, there will come a day where you will face a problem more difficult than how to divide pizza”.

“Gnarly!”, Michelangelo shouts.

“Mikey, let him talk!”

“OK, Raph, just get your sai away from my slice. Master, please, continue”

Splinter looks around. “As the problems become more complex, Donatello’s techniques will still become increasingly hard to reason about. What you need is a way to divide up your program code into small, reusable pieces that perform specific functions- and can be composed without any lockin to a specific hierarchy. That way you can focus on one problem at a time — no matter how big the sum of all those problems gets.”

“For example, if we look at Leo’s code — we see there’s actually a few separate functions. Input validation, number conversion, and so on. For starters, let’s refactor like this:”

Better but not there yet. Like great cheese and no sauce

Leo takes a look and points out, “master, I see that you’ve moved the calculations out of dividePizza(), but honestly it is still…”

“Mondo confusalicious!”, Mikey chimes in.

“Exactamondo!” Raph adds.

“Patience! You are right, and we will make it better. However, this alone can make a great difference in your code’s clarity. It’s true that dividePizza() is still a mess, but imagine if those calculations in the different functions were more complicated. Isolating them into their own functions for testing and debugging is more powerful than it seems here. The purity of these functions, which only depend on the values given them, allows their re-use in all kinds of code with no changes at all! For example, we could use getInputValue() for any object, getNumberFromString() for any number, etc.”

“Though, you are right — in this case we haven’t done much to make things better. So where do we go from here? Let’s look to Robert Frost:”

TWO roads diverged in a yellow wood,
And sorry I could not travel both
And be one traveler, long I stood
And looked down one as far as I could
To where it bent in the undergrowth;

“If we look at our original problem, it boils down to a tree of branches, just like Frosts’ poem (haha I made a funny!) - specifically branches with TWO choices at each juncture. Ultimately, the problem we want to solve can be described like this:”

How do we call a series of functions that either pass or fail, such that:

  1. A successful function passes its output as the input to the next function
  2. A failed function prevents further functions from being called
  3. A failed function causes the final output to be its failure message

“Now, some of Donatello’s techniques can be used to solve this. But again, we want our functions to be composable and pure so that we can reuse and reason about them easily. We want this rule to apply even if we divide our functions up in different ways. We want the control flow of this structure to be simple and something that follows the same purist paradigm (or can even abstracted entirely). What we’d like, is something that looks like this (where each f is one of our functions):”

const result = f4(f3(f2(f1(formInput))));
if(isError(result)) {
logError(result);
} else {
log(result);
}

“We’d like to follow that pattern with any function we throw in there- not just our custom pizza slicing code. Furthermore, the function-calling code should look completely identical whether it’s dealing with a failure case or success case.

“Master?” Donatello asks, “respectfully, that doesn’t make any sense to me at all. If the functions expect one kind of input, like a successful value, then how can they work unchanged when given an error value?!?!?!?

“Very good Donatello! That’s exactly what we must solve!”

“Master?” Michelangelo asks, “can we eat yet?”

“Soon my boy. Very soon.”

“Donatello — the code we have right now would indeed be very difficult to adapt meet our goal completely. We need a technique that controls which of the two roads to go down — the one that’s grassy or the one less traveled by. The ability to choose which road — at any point, and follow it to the end, will truly make all the difference.”

“In order to implement this in code, we’re going to use concepts from Functional Programming and a functional javascript library called Sanctuary. Although we’re going to be using things called Monads and Combinators and algebraic formula under the hood — we don’t need to worry about the theory behind it. They’re just functions and objects like any other. In fact, once you get the hang of it, programming this way becomes much more focused and productive.”

“Here’s how our code is going to look when we’re done:”

Now we’re cookin’

“While it may look a little strange — the key takeaway is that we can call dividePizza() with both error values and success values, and simply work with the result — whatever it may be, at the end of the road. Additionally, the code inside of dividePizza() (and the functions it calls) is easy to reason about — it’s just a sequence, passing the data from one function to the next.”

Mikey’s eyes, and nose, perk up. “I see… curry() — is that going on the pizza?”

“Sortof. To make this work we use a few specific functions from the Sanctuary library. Let’s learn more about them.”

Cowa…”

“No, no… let me — Cowabunga!”

Part 2: Tools of the ninja — pipes, chains, and monads

(coming soon)

— — — — — — — — — — — — — — — — — — — — — —

Thanks to the following for teaching me this technique:

  1. David Chambers, Aldwin Vlasblom, Matthew Willhite, and everyone in the Sanctuary Gitter.
  2. This talk by Scott Wlaschin: Railway Oriented Programming
  3. Professor Frisby’s Mostly Adequate Guide (especially this section)

Further Reading:

  1. The Sanctuary API
  2. The Fantasy-land spec
  3. Functional Programming In JavaScript — With Practical Examples