This article is more than 1 year old
Learn you Func Prog on five minute quick!
I remember the Monoids; they came before the Cybermen
Stob Functional Programming is the great paradigm shift that is sweeping through software development.
The object-oriented era of coding is at last drawing to an inevitable and shameful close. We can – and we must! – all join hands and skip gaily into a brave new world, where sloppy state and careless side effects are excised without ruth.
Now every program, from humble Android applet to mighty engine-pollution-test-falsifier, must slough off ridiculous trappings and clumsy complexities of objectivity, and float elegantly forwards down the shiny new functional pipeline – the one, true alimentary canal of code to concurrent computing paradise.
Unfortunately, in order to join in whole-heartedly in this cultural revolution, it is imperative (yes, yes I know) that OO programmers discard all their sentimental objections and adopt the new One Right Way.
So, Sibling Coder, close down your Facebook app and lay aside your tweets. I have prepared a brief summary of the key FP tenets which even you can absorb and inwardly digest in a few minutes.
Then, when the Red Functionaries come a-knock-knock-knocking, we two can carry your large, carefully-designed class hierarchy framework out into the street and together burn it on the communal bonfire while shedding tears of revolutionary happiness.
Choice of Weapon
The first decision to be made is which programming language to use. Here is a table to help.
Language | Pros | Cons |
---|---|---|
Erlang | The original practical FP language that has really been put through its paces in the field. By reputation so robust you can change boards in the machine on which an Erlang program is running with no deleterious effect. Or switch the whole thing off, and drop it in a bucket of ice water. | Syntax is incomprehensible gibberish. |
Haskell | A shining, elegant-as-a-snowflake, expressive-as-Dame-Maggie-Smith language of immense power and no dirty compromises. Pure. Hugely influential. Its very functional farts smell of fresh strawberries. | Primarily a CV decoration language. Code is typically written to be admired rather than compiled; this is technically known as the "lazy execution model."
Besides, you won't understand it. |
Scala | Close relationship with Java, and underlying JVM implementation, means it offers all the features of that platform. | Close relationship with Java, and underlying JVM implementation, means it offers all the features of that platform. |
F# | A Microsoft .NET language with roots in ML. | Redundant to write anything here, because you have already made up your mind. Haven't you? |
Clojure | JVM platform with ports to .NET and JavaScript. | It's just LISP in functional clothing, for heaven's sake. This, in the Year of Our Ford Quarter Past Eight. Will those pesky Lispers never desist?
Mark my words: next stop, FUNCOBOL |
JavaScript | It's what you'll end up using. | The most dysfunctional functional language. On top of all its well-known flaws that disrupt ordinary primitive programming, it lacks many allegedly must-have functional gewgaws. Its variables are mutable, its type holes are indubitable, its 'eagerness' unsuitable, its modules are inscrutable, tail recursion disputable, scoping inexcusable, and I'll stop there, with 'refutable' (to name but one) still in the bank, just because you were kind enough to beg me. |
State, Purity and the Functional Way
As you know, the functional programming movement was founded by a terrorist wing of a mathematical discipline called "category theory." Its bylaws and regulations therefore are established by carefully derived proofs and axioms, which are impossible for unqualified persons to understand.
The following bluffer's guide, however, requires no such discipline or understanding; it was hastily and carelessly constructed by skimming the Amazon blurb for Haskell books. Enjoy.
Rule 1: You can assign to a variable exactly once. Thereafter variable and value are joined together until death do them part.
Rationale: This makes it easier to reason about the code.
OO programmer's incredulous reaction to rule 1: What, are you transpiling my polyfill? I've got a modifiable state the size of Liverpool here. Works like a charm. Hands off!
Consequence: If pursued to its puritanical end, this rule – apart from other disadvantages – leads to a disgusting mess of poorly-named, barely-used identifiers strewn all over the code base, like the campsite field the day after a rained-off rock festival.
Observation for the cognoscenti: The actual rule 1 is: “You can assign to a local variable as many times as you like, provided you aren't caught. ”I found this heresy, suitably rephrased, on p150 of Functional JavaScript, so we deffo have something for the defence counsel when it goes to court.
Rule 2: All functions must be 'pure', meaning they must be blond and blue-eyed and not touch any external state. Their behaviour is solely governed by their arguments.
Rationale: This makes it easier to reason about the code.
Consequence: Even FP puritans confess this makes the implementation of such things as I/O and random number generators "tricky." But that isn't the half of it. If you are not careful, even with the most innocuous-seeming program, you end up passing around the world in your function parameters.
I highly recommend this amusing-to-techies sequence of essays in which blogger James Hague sets out to re-implement the old arcade favourite Pac-Man using Erlang. As a friend of Hague's astutely remarks, this feels about as rewarding as writing a novel without using the letter “e”.
Observation: To paraphrase the late Terry Pratchett, maybe we were a bit forward making this an actual rule. Perhaps we could downgrade it to a long term ambition, and then bury it in the fourth paragraph of the mission statement.
Rule 3: Functions should be curried.
Rationale: This makes it easier to reason about the code.
Background: "Currying" was of course named after the implausibly-monikered mathematician Haskell Curry (who seems to have attracted more than his fair share of fame, compared with his colleagues Prof Ruby Iterator and Dr P.H.P. Unexpected-Exception). The effect of currying can be seen by making a trivial function, say:
function multiply(x, y) { return x * y;}
and then calling it without its second parameter. In real life, of course, you get the Not-a-Number times table:
multiply(1); // NaN
multiply(2); // NaN
multiply(3); // NaN
but in fictional, sorry, functional languages such as Haskell you are instead delivered new, somewhat limper functions, each taking a single argument and multiplying by one, two or three.
Like this:
f1 = multiply(1); // f1(x) can be used to multiply by one
doubleFunc = multiply(2); // doubleFunc(x) saves you writing 'x * 2'
tripler = multiply(3); // tripler(x) returns... oh, you surely get the idea
Such is the magic of curry.
The functional mafia would have you assign these piffling, half-called, chewed-and-spat-out functions to dozens of new, single-shot variables declared for the purpose, and use them to help clutter up your code even more.
Advice: There are JS libraries that will do all your currying for you, e.g. Ramda.js. Or you could grind your own, if you prefer.
However, we recommend the lazy (sic) approach. When picked up on a curryless violation by an FP cop, the thing to do is to cock one's head to one side, put on an expression of innocent enquiry, and say: “But is that really currying? Or is it merely partial function application?”
This will trigger the Involuntary Explanation Reflex, and you will be able to creep away to safety under cover of a one-sided discussion of the analytical advantages of mono-arity functions.
Monads and Nomads
Rule 4: The Curse of the Monad.
History: The curse was first articulated by Douglas Crockford the JSON King, no less. He deduced it after reading many blogger explanations of monads and, we suspect, Catch-22.
Nub: If you should by some accident come to understand what a Monad is, you will simultaneously lose the ability to explain it to anybody else.
Observation: Crockford himself has vowed to overcome the curse. You can judge for yourself whether he succeeds by viewing his 50-minute talk "Monads and Gonads."
(Executive summary: no.)
Imperfect logic: I can do better than Crockford, because, having the good fortune to be, as it were, a Monadic virgin, the curse's precondition is non-applicable.
Reader's comment: Hmm.
Rule 5: FP is mostly accomplished with monads. A monad is a programming entity, defined with a few, easily-understood properties.
Rationale: Monads make it easier to reason about the code.
Further Reader's comment: Get on with it.
Canonical Monadic Properties:
Monad property 1: A monad is kind of like a common-or-garden variant, i.e. it is an object wrapped around a value. Except it probably won't tell you what its value is, unless you put your hand in its mouth. Think of a cocker spaniel that won't give its ball back.
Monad property 2:
Monads
.do()
.that()
.annoying()
.chained()
.call(thing)
.like(jQuery); // and bad modern poetry
Monad property 3: That's not to say JQuery is monadic or even slightly monoidal. Hoh no. Deffo born the wrong side of the type system, jQuery. Where the comonads live.
Monad property 4: Monads rarely throw exceptions. Instead they prefer to pretend nothing happened and pass the Empty Monad down the chain. This improves the level of surprise when you get to the end of a long calculation and find that, instead of a nice diagnostic stack trace or the actual answer, you have the computing science equivalent of a belch. But never mind. Admire the smooth, alimentary flow of the functional pipeline that delivered it!
Monad property 5: Because they are very important, monad names are preceded by the definite article. The List Monad, the I/O Monad, the Continuation Monad, the Maybe Monad (a rare transgender monad), and the YourTurnToDoTheWashingUp Monad.
Monad property 6: Monads are by definition composable, malleable and ductile. No, half a mo, that may be lenses. Or functors. Or acids.
Monad property 7: All monads define a unit() function called of (), a bind() function called map() and a type constructor function called...
Wait a minute. Wait a minute. Perhaps bind() is a functor not a function. I'm pretty sure about that. Hold on to the horses a moment there while I look it up.
...And I should perhaps clarify that this bind() and map() is nothing to do with any other bind() or map() methods or functions that you might be familiar with, although their actions are in some sense quite similar.
Summary: It has been an honour and a pleasure to clear all that up for you.
Final Reader's comment: My gratitude is inexpressible.
Revision Exercises
- Distinguish between a first, second and third class functions. Provide examples. Why is it neither clever nor funny to call a lower order second-class function a 'Desmond' function?
- Do you fold to left or to the right? Do not provide too much information. Ugh.
- Pointfree or pointless?
- A tuple, a monad, a monoid and the Thrush combinator go into a bar together. Show what happens next, with diagrams.
- "Using Underscore.js or Lodash is an excellent way to get a start in FP." Disparage this sentiment vigorously yet patronisingly.
Next: How to simplify the handling of a mouse click by instead treating it as a cell from a combination of infinite-length arrays. I bet you can't wait, can you? ®