On Github killtheliterate / functional-js-wut
In which I expound on things that are interesting to me.
Garrett Dawson
@killtheliterate
I've been a front-end dev of 5 years. I work at VictorOps
I started in Drupal, accidentally became a PHP developer.
I Decided to refocus on JS, and have been learning a lot by studying FP
I'm a beginner. I hope I always will be.
It is a programming style that "...produces abstraction through clever ways of combining functions" [1]
What is abstraction? It's a less verbose way of describing the solution to a problem
Why is abstraction good? It allows us to write more generalized solutions. DRY
At it's core, the function is an abstraction. It encapsulates some operation we would like to do over and over
How is abstraction produced using this style of programming?
By Leveraging first class functions and higher-order functions
...also, by pretty much hating global state
Straight-up. So, we'll need a functional language, then.
Obviously, the things we've talked about need to be facilitated by the programming language
Some languages are purely functional, others are a mash-up
I'll be using JavaScript as the language of example
FP is such a good idea, many languages incorporate things like lambdas
First class functions are values, and can be used in every way that typical expressions can
They are values. The benefit from all the advantages of being values. Code = data
Assignment, return, argument, etc.
Can be assigned to variables
var foo = function() {return 'hai'}; foo(); // "hai"
You can see here that we are assigning a function to a variable.
This is the essence of "lambdas"
Note that it's not the return value of the function that is assigned, but the function itself.
Expression vs. declaration. Declarations are basically destructured to expressions
Can be returned by functions
var bar = function() { return function() {return 'hai'}; }; bar(); // function () {return 'hai'}
Note that we assign a function to the variable bar
And when we call the function referenced by bar, we get another function
Can be passed as arguments to other functions
var baz = function(func) { return func(); }; baz(function() {return 'hai'}) // "hai"
Here, we add a function to our argument list. It gets fired off from the original function, since it's been aliased to func
Probably looks a bit familiar. Like a callback in an event handler
Use functions as its parameters and/or return a function
var bar = function(func) { return function() { return func(); }; }; bar(function() {return 'hai'})() // "hai"
Functions that operate on functions
Notice the immediate function invocation at the end
Why functionally program? And why do it with JavaScript?
I'll go over some of the motivations to learn the functional aspects of JS
First, lets check that JS meets our criteria for FP
Since FP is core to JS, you need to know it to be good at JS
Turns out, it's one of the best features of JavaScript
Small functions are testable
Small functions are single responsibility, less cognitive load
Well encapsulated code is elegant
it's a style, not a syntax
It gives you a powerful paradigm that may lead you elsewhere
How, functional JavaScript?
We meet our criteria, let's see some examples
These "techniques" are built of one another
We know:
With first class functions, suddenly you have a lot of tricks you can pull off
I'll talk about some of those tricks, why they're useful.
Stateful functions mutate state outside of their scope. FP avoids mutation.
var number = 1; // This is NOT functional programming var increment = function() { return number += 1; }; increment(); // 2 increment(); // 3 increment(); // 4 * Stephen Young [2]
First up, let's talk about how state is dealt with in FP
Stateless functions don't mutate state outside their scope
var number = 1; var increment = function(n) { return n + 1; }; increment(number); // 2 increment(number); // 2 increment(number); // 2 * Stephen Young [2]
Async!
This function looks almost the same as before, except this time we're passing an argument
We want a function to always give the same value back.
We can build pipelines this way
Mutating shared states can have unexpected consequences
Shared state makes async programming hard. Maybe impossible
To understand recursion, you must understand recursion.
*Anonymous
So, if we're stateless, how do we loop?
One way is to leverage recursion
Functions that call themselves
var loop = function(n) { if (n > 9) { console.log(n); return; } else { console.log(n); loop(n + 1); } }; loop(0); * Stephen Young [2]
Notice that we're not using a for or while loop, because they are stateful
Applicative programming is a technique that allows a function to be applied to each element of a list
*Mary Simoni [3]
More looping!
Looping on lists is crucial
Array.prototype.map()
var squared = [1, 2, 3, 4, 5].map(function(el) { return Math.pow(el, 2); }); // [1, 4, 9, 16, 25]
Projection
We're not mutating the array. We're storing our return in a new bucket.
Array.prototype.filter()
var even = [1, 2, 3, 4, 5].filter(function(el) { return el % 2 === 0; }); // [2, 4]
Array.prototype.reduce()
var sum = [1, 2, 3, 4, 5].reduce(function(memo, curr) { return memo + curr; }); // 15
Projection
Tricks like algebraic logic. Can reduce to true/false.
Can reduce a list to another type. Array to Object
A predicate determines whether or not something is true or false.
var isNull = function(obj) { return obj === null; }; isNull(null) // true
Array.prototype.filter() could use a predicate.
Closures are functions that refer to independent (free) variables... the function defined in the closure "remembers" the environment in which it was created.
*Person that wrote the MDN page[5]
function stringCount(string) { return function(subString) { return string.match(new RegExp(subString, 'g')).length; }; } var count = stringCount('sup sup sup'); count('sup'); // 3
Closures are much easier than they are made out to be, because a lot of folks are introduced to them in the wrong way.
Function context (call-site) and lexical scope (variable lookup) are why JS has closures.
Closures are why we can curry functions in JS.
Currying is the act of taking a function that takes more than one argument and converting it to an equivalent function taking one argument.
*Reginald Braithwaite [3]
var foo = function (x) { return function (y) { return function (z) { return x + y + z; } } } var bar = foo(1) // partially applied function bar(2)(3) // 6
It’s really that simple: Whenever you are chaining two or more functions together, you’re composing them
*Reginald Braithwaite [4]
var compose = function(a, b) { return function (c) { return a(b(c)); }; }; var addOne = function(number) { return number + 1 }; var double = function(number) { return number * 2 }; var addOneAndDouble = compose(double, addOne); // right to left addOneAndDouble(2); // 6
This is one of my favorites. Notice it's leveraging partial application.
This ties into the idea of DRY. Make symbol, general purpose functions that you combine into more complex or specific functions.
(AKA Easy Mode)
Don't need libraries, but because FP is made up of many common idioms, it's nice to not have to write them.
var numbers = _.filter([1, 'foo', 3, 'bar', 5, 6], _.isNumber); // [1, 3, 5, 6] var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); // [2, 4, 6]
RTFM. The underscore annotated source is SOOOOOO good
var evens = ramda.filter(function(num){ return num % 2 === 0; }); evens([1, 2, 3, 4, 5, 6]); // [2, 4, 6]
There is a lot to know about FP