By Francis Murillo
On Apr. 14, 2016
At Manila, JavaScript #06
https://github.com/FrancisMurillo/the-power-of-functions-in-javascript
This is the original source of the presentation, the revised cut I used for the presentation is the presentation-shortened.org.
I had hoped to be able to give this in 30 minutes but I failed to realize that I think faster than I talk, showed too much code and tried to discuss a wide range. A valiant effort.
I hope this presentation will help you write good code and with a different lens. Thanks.
- You know the basics of JavaScript syntax
- I am talking about JavaScript in the browser
- You and I hate presentations that waste time
Just a mediocre software developer.
Judge a man by his questions rather than his answers
– Voltaire
Who here thinks JavaScript (in the browser) sucks?
Where do I begin?
- Lousy type system
- No module system
- Global by default
- No block scope
- Monkey patching
- The rest couldn’t fit on one slide
But I am not here to bash on the language.
How do we write good code in a (sucky but awesome) language?
How do write code that is…
- Modular
- Extensible
- Reusable
- Readable
- Poetry
JavaScript already has a concept for that
It’s not a framework, or a library or a new concept and it’s been around since ancient times
The building blocks of programs
var theAllPowerful = function () {
return 'Bow before me';
}
The best thing about JavaScript is its implementation of functions. It got almost everything right. But, as you should expect with JavaScript, it didn’t get everything right.
– Douglas Crockford
In order to understand their supremacy, let’s review what functions look like in JavaScript first.
Just a brief review of the following.
- Functions
this
andarguments
keywordcall
andapply
function- Closure
- Recursion
The bread and butter of programmers
There are two primary ways to declare a function.
// Function statement
function greetLucy() {
return 'Hello Lucile';
}
// Function expression
var greetPatty = function _greetPatty() {
return 'Hello Sir';
};
// Function Constructor
// We never ever talk or even write this as this is as evil as eval()
// This is just for completion
var greetLinus = new Function ("return 'Sweet Baboo';");
There’s three known ways to call or invoke a function.
// Math yo
var add = function _doTheMath(x, y) {
return x + y;
};
// The canonical function invokation
add(1, 2);
// The functional way of calling functions
// We'll get to this later
add.call(null, 1, 2);
// Same thing above but the arguments is passed as an array
add.apply(null, [1, 2]);
return
is the output of the function. If there is no return
, an
implicit undefined
is the output.
function returnSomething(x) {
return 'I call ' + x.toString();
};
var noReturn = function () {
// do something
}
console.log(returnSomething('Pikachu')); // out: 'I call Pikachu'
console.log(noReturn()); // out: undefined
It is the compiler putting variable/function declarations on top(without your consent)
Consider this snippet consisting of a function statement
doWeirdStuff()
function doWeirdStuff() {
console.log('Wait, this is declared after it is invoked.');
}
Compare this with the function expression equivalent
doWeirdStuff()
var doWeirdStuff = function theWeirdStuff() {
console.log('This is the same right? It should also work. Right?');
}
The first one executes, while the other one gives a undefined type error.
Prefer function expression over function statement to avoid hoisting.
var theLongerWay = function isTheSaferWay() {
return true;
};
Every function has two implicit variable: this and arguments
var MyModule = {
whatIsThis: function () {
// What is?
console.log(this);
// And what is
console.log(arguments);
}
}
MyModule.whatIsThis(1, 2, 3, 4);
// out(this): MyModule
// out(arguments): [1, 2, 3, 4]
It is just simply the method’s object
var MyObject= {
myValue: 'Nyan Cat',
myMethod: function () {
console.log(this);
console.log(this.myValue);
}
};
MyObject.myMethod();
// out(this): { myValue: 'Nyan Cat', myMethod: function ()}
// out(this.myValue): 'Nyan Cat'
An array-like object containing the arguments
var showMeTheEvidence = function (a, b, c) {
var args = arguments;
console.log([a, b, c]);
console.log(args);
}
showMeTheEvidence(1, 2, 3) // out: [1,2,3]
showMeTheEvidence('This', 'is', 'nuts') // out: ['This', 'is', 'nuts']
// Not enough arguments? That' weirdly okay. Javascript is free
showMeTheEvidence({ x: 1 }, true);
// out([a, b, c]): [{x:1}, true, undefined]
// out(arQguments): [{x:1}, true]
// Too many arguments? That's free of charge too
showMeTheEvidence(true, false, 0, 1)
// out([a, b, c]): [true, false, 0]
// out(arguments): [true, false, 0, 1]
It is not an array
var extraRicePlease = function (/* args */) {
var args = arguments;
args.push('Extra rice');
console.log(args);
};
extraRicePlease('BBQ', 'Ice Tea');
// expected: ['BBQ', 'ice Tea', 'Extra Rice']
// out: arguments has no method push
Convert arguments
to an array using Array.prototype.slice.call(arguments)
.
var extraRicePlease = function (/* args */) {
var args = Array.prototype.slice.call(arguments); // Why must we do this
args.push('Extra rice'); // Now it works
console.log(args);
};
extraRicePlease('BBQ', 'Ice Tea');
// out: ['BBQ', 'Ice Tea', 'Extra Rice']
Use can also use the shorter form [].slice.call(arguments)
.
Check out this sketch that counts something globally and locally.
var Counter = {
globalCounter: 0,
createCounter: function() {
return {
localCounter: 0,
increment: function() {
this.globalCounter += 1;
this.localCounter += 1;
return this.localCounter;
}
}
}
};
var buttonCounter = Counter.createCounter(),
ajaxCounter = Counter.createCounter();
console.log(buttonCounter.increment()); // out: 1
console.log(ajaxCounter.increment()); // out: 1
console.log(Counter.globalCounter);
// expected: 2;
// out: 0;
Be careful using this
. If you use functions within functions or
objects within objects, it is safer to save it in a variable
preferrably that
or the Pythonic self
. Likewise for arguments
but very uncommon.
var Counter = {
globalCounter: 0,
createCounter: function() {
var that = this; // Store the parent refrence
return {
localCounter: 0,
increment: function() {
that.globalCounter += 1; // Correct reference
this.localCounter += 1;
return this.localCounter;
}
}
}
};
Every function has the method call
and apply
to invoke them functionally
var fullName = function (firstName, lastName) {
// return [firstName, lastName].join(' ');
return firstName + ' ' + lastName;
};
// Normal invokation
console.log(fullName('Francis', 'Murillo')); // out: Francis Murillo
// Using call() without the
console.log(fullName.call(null, 'Mr', 'Robot')); // out: 'Mr Robot'
// Using apply()
console.log(fullName.apply(null, ['Jake', 'Dog'])); // out: 'Jake Dog'
The first argument of call
and apply
sets the this
keyword, this is more important for the OO style.
var Mirror = {
whoAmI: 'Tyler Durden',
whoAreYou: function (prefix) {
console.log(prefix + this.whoAmI);
}
};
var Bad = {
whoAmI: 'Heisenberg'
};
Mirror.whoAreYou('I am '); // out: 'I am Tyler Durden'
Mirror.whoAreYou.call(Bad, 'You are '); // out: 'You are Heisenberg'
var VeryBad = {
whoAmI: 'You only need to know mine'
};
Mirror.whoAreYou.apply(VeryBad, ['I do not need to know your name']); // out: awesome
Some practical uses of setting the this
argument.
var showcaseThis = function (/* args */) {
var args = arguments;
// Case: Converting arguments to an array, reasonable
console.log(Array.prototype.slice.call(this, args));
};
showcaseThis('Clockwork', 'Orange');
// out: ['Clockwork', 'Orange']
// Case: Invoking a "class"/prototype method on an instance
// Why not just "Beethoven".toUpperCase()?
console.log(String.prototype.toUpperCase("Beethoven"));
// out: BEETHOVEN
// Easier to just ['Ode'],concat('to', 'Joy');
console.log(Array.prototype.concat.call(['Ode'],'to', 'Joy'));
// out: ['Ode', 'to', 'Joy']
// Case: Hacking an "private" variable
// Wait for it
Consider this quaint way of invoking a method
var Cat = {
affection: 9007199254740992;, // It is at least over 9000
pet: function _pettingTheAdorablePuspin() {
if (this.affection > 50) {
console.log('purr');
} else {
console.log('hiss');
}
}
};
var petMethod = Cat.pet;
// Working at the office
// Home and tired, I want to relieve stress. Must pet cat
petMethod();
// expected: 'purr'
// out: 'hiss'
Be aware if a function needs an object reference or not.
petMethod(); // out: 'hiss'
// Because this.affection is undefined
// undefined > 50 is false
// This should be a type error!?
petMethod.call(Cat); // out: 'purr'
// If you know the object, then why not just invoke it directly
Cat.pet();
For this presentation, we will not use this
and invoke call
and
apply
with null as the first argument whenever needed.
Functions have access to variables to it’s outer function.
var outerFunction = function (outerArg) {
var outerVar = outerArg + 10;
return function innerFunction(innerArg) {
var innerVar = outerVar + innerArg;
return [outerArg, outerVar, innerArg, innerVar];
}
}
var newFunction = outerFunction(10);
// outerArg: 10
// outerVar: 20
console.log(newFunction(20));
// out: [10, 20, 20, 40]
console.log(newFunction(100));
// out: [10, 20, 100, 120];
We’ll talk about this more later
Not really going to talk about this, but it exists.
setInterval(function myLovelyWidget() {
var data = []; // Possibly never garbage collected
fetch('someUrl') // Promise like behavior
.then(function getAndRender(newData) {
data = newData;
// render new data
// do something else
});
}, 1000);
Calling a function over and over again
// Naive quicksort
var quicksort = function _quicksorter(xs) {
if (xs.length <= 1) { // Base cases
return xs
} else if (xs.length === 2) {
var first = xs[0],
last = xs[1];
return first <= last ? [first, last] : [last, first];
} else { // Dividing the task
var pivot = xs[0],
less = xs.filter(function (x) { return x < pivot; }), // Spoiler Alert
more = xs.filter(function (x) { return x > pivot; }),
equal = xs.filter(function (x) { return x === pivot; });
return []
.concat(_quicksorter(less))
.concat(equal)
.concat(_quicksorter(more));
}
};
console.log(quicksort([4, 1, 2, 3])); // out: [1, 2, 3, 4]
Invoking the parent or calling function using the name given in the function declaration.
var factorial = function _factorer(n) { // Notice the name in the function
if (n <= 1) { // Base case
return 1;
} else { // Induction case
return n * _factorer(n - 1); // Invoke the parent function
// return n * factorial(n - 1); // Also okay but relies on external binding
}
};
console.log(factorial(3)); // out: 3 * 2 * 1 => 6
console.log(factorial(5)); // out: 5 * 4 * 3 * 2 * 1 => 120
Be very careful within recursion as it can cause an infinite recursion with rogue or incorrect input.
var countDown = function _counter(n) {
if (n === 0) { // Base case
console.log('Blast off!!');
} else {
console.log(n);
counter(n - 1); // Recursion
}
};
countDown(3);
// out: 3
// out: 2
// out: 1
// out: 'Blast off!!'
countDown(3.001); // Unhandled case
// out: 3.001
// out: 2.001
// out: 1.001
// out: 0.001 // Where's the smoke?
// out: -0.999 // Wait... when is the rocket launching?
// out: -1.999 // Houston, we have a problem
Since JavaScript does not support functional programming up front, just be careful when choosing to implement a problem with recursion.
For this presentation, I won’t delve into recursion but for just know and remember you can do recursion… with love and care.
Good design is not about making grand plans, but about taking things apart.
– Rich Hickey
At last, let’s talk about functions.
I don’t want to bore you with a definition. It’s really just a mindset
- Input and Output
- Combining Functions
- Process over Step
- Everything is a Function
- Avoiding State
- First principles
- Easier to write good code
- Easier to reason
- Natural and Mathematical
- Fun and Creative
Christianity has not been tried and found wanting; it has been found difficult and not tried.
– Gilbert Chersterton
- State
- Classes are heavyweight
- Don’t know the prototype system
- Overrated
- A full course in this paradigm
- A pure functional style
- An introduction to JavaScript
- Easy
To introduce you to think and write in a functional style can help you write good code in JavaScript and nothing more.
What is the most resilient parasite? Bacteria? A virus? An intestinal worm? An idea. Resilient… highly contagious. Once an idea has taken hold of the brain it’s almost impossible to eradicate. An idea that is fully formed - fully understood - that sticks; right in there somewhere.
– Cobb from Inception
The whole of the presentation
- Thinking in terms of collections or as a whole
- Separating behaviors and combining them
- Avoiding state with closures
You do not have to understand the code, just feel and see that this is much cleaner and better as a whole.
Let’s talk about lists and their functions namely
Spoilers
.forEach
.map()
&.mapObject
.toPairs
&.fromPairs
.filter
&.filterObject
.reduce
Who here uses a for loop like so?
var items = ['Hey Arnold', 'Adventure Time', 'JavaScript: The Good Parts'];
for (var i = 0; i < items.length; i += 1) {
console.log(item[i]);
}
Intention
When iterating through an list, one should not be concerned with the length and index of it. All you need is the element and do something with it.
var heroes = [ 'Cloud', 'Tyler', 'Joel'];
for (var i = 0; i < heroes.length; i+= 1) {
var hero = heroes[i];
if (hero === 'Tyler') { // The intent
console.log('Everything is free of charge.');
}
}
How do we loop over each element without being concerned with the loop mechanics?
Our journey starts by using the forEach method of an list which allows you to loop through a list given a callback.
var assassins = [ 'Altair', 'Ezio', 'Connor'];
// Use forEach to iterate thorugh a list
assassins.forEach(function getConnor(assassin) { // Give the function a descriptive name
if (hero === 'Connor') {
console.log('Where is Charles Lee');
}
});
Now this looks better, we separated the intention and the looping mechanism is now hidden as an abstraction.
But did you notice we passed a function to another function(the callback).
The bare minimum that allows JavaScript to be functional is that functions are first class objects meaning you can pass functions as arguments and as variables.
var myHandler = function (x) { // Assigning functions to variables
return 'You shall not pass';
};
var executeHandler = function (handler) { // A function taking a function
handler('An ignored value');
}
executeHandler(myHandler); // Passing functions around
If JavaScript did not have this feature, we would not be talking about it.
Let’s implement our own forEach
for analysis
// Naive implentation
var forEach = function (f, xs) {
for (var i = 0; i < xs.length; i+=1) {
var x = xs[i];
f.call(this, x);
}
};
var pi = [3, 1, 4, 1, 6, 1, 8];
// Almost looks the same
forEach(function displayTheNumber(n) {
console.log(n);
}, xs);
Pretty easy but this demonstrates how passing functions can improve the logic
Let’s move on a similar case of transforming the collection.
How about given a list of text, capitalize each one.
var words = ['What', 'Are', 'Words']; // Base list
var upperWords = []; // New list which is just a placeholder
words.forEach(function capitalize(word) {
var upperWord = word.toUpperCase();
upperWords.push(upperWord); // Add the value to the list
});
console.log(upperWords); // out: ['WHAT', 'ARE', 'WORDS']
In this case, we want to capitalize the whole list, not capitalize each word in the list.
The intention of the problem if we define it as a function.
var capitalize = function theLoopIntention(text) {
return text.toUpperCase(); // Notice our function has a return
};
What if we can just apply it as a whole instead of defining it?
Thankfully the map method of lists allows this.
var words = ['What', 'Are', 'Words']; // Base list
var capitalize = function theLoopIntention(text) {
return text.toUpperCase();
};
// Just like forEach
var newWords = words.map(capitalize);
// or if you want to inline it
// var newWords = words.map(function (word) {
// return text.toUpperCase();
// });
console.log(newWords); // Like the last
Again, we have cleanly separated the intention from the loop.
Just some examples to get improve comprehension
var people = [
{ firstName: 'Linus', lastName: 'Van Pelt', nickname: 'Sweet Baboo'},
{ firstName: 'Charlie', lastName: 'Brown', nickname: 'Blockhead' }
];
var getNickname = function (person) {
return person.nickname;
};
var getFullName = function (person) {
return person.firstName + ' ' + person.lastName;
};
var capitalize = function theLoopIntention(text) {
return text.toUpperCase();
};
// Case: Getting a property from a list of objects
console.log(people.map(getNickname)); // out: ['Sweet Baboo', 'Blockhead']
console.log(people // You can chain maps by the way
.map(getFullName)
.map(capitalize)); // out: ['LINUS VAN PELT', 'CHARLIE BROWN']
Just like with forEach
, implenting the map
is easy.
var map = function (f, xs) {
var ys = [];
xs.forEach(function (x) {
var y = f(x);
ys.push(y);
});
return ys;
};
var wrapAsText = function (x) {
return '<p>' + x + '<p/>';
};
var labels = ['First Name', 'Last Name'];
console.log(map(wrapAsText, labels));
// out: ['<p>First Name</p>', '<p>Last Name</p>'];
Again we separated the intention from the implementation. Isn’t this a recurring theme?
The user of the function should only be concerned with the input and output, not how it is done. It is transforming the inputs to produce the output.
In the case of map
, given a list and a transformation function,
return a transformed list. You should not care how it did it, be it a
for loop or a forEach function or a recursion.
This is both hard to explain and do. Let’s demonstrate this with the next concept.
You can think of objects as a collection of key value pairs
var object = {
'cowboy': 'bebop',
'stein': 'gate',
'summer': 'wars'
};
var objectPairs = [
['cowboy', 'bebop'],
['stein', 'gate'],
['summer', 'wars']
];
So why should it matter? Patience, let’s define what pairs are.
Pairs are just lists with two elements. Easy, right?
var numberPair = [100, 20],
stringPair = ['Satoshi Kon', 'Tokyo Godfathers'],
keyValuePair = ['key', { subObject: 1}],
pairInPairs = [[1, 2], [3, 4]]; // Not going to go that far
var numericTriple = [1, 2, 3], // Three elements
singleString = ['Working!!!']; // One element
Let’s create a function to convert an object to a list of key value pairs
// Assuming we don't have Object.keys
// This returns the keys of an object
var keys = function (object) {
var keys = [];
for (var key in object) {
if (object.hasOwnProperty(key)) {
keys.push(key);
}
}
return keys;
};
// Making the get a function
var get = function (key, object) {
return object[key];
};
// Converts an object to a list of key value pair
var toPairs = function (object) {
return keys(object)
.map(function covertToPair(key) {
var value = get(key, object);
return [key, value];
});
};
Just a demonstration.
toPairs({
number: 1,
text: 'meow',
isSomething: true,
recurse: {
A: 'a',
B: 'b'
}
})
/*
out: [
['number', 1],
['text', 'meow'],
['isSomething', true],
['recurse', {
A: 'a',
B: 'b'
}]]
]
*/
Let’s create a function to do the opposite: convert a function
// Let's wrap the set of objects as a function
var set = function (key, value, object) {
object[key] = value;
return object;
};
var fromPairs = function (pairs) {
// The recursive version is much more functional but not necessarily better
var newObject = {};
pairs.forEach(function (pair) {
var key = pair[0],
value = pair[1];
set(key, value, object);
});
return newObject;
};
So what does it mean to think of objects as pairs?
What if you had an object and want to capitalize every string?
What if we could map over over objects as we do with lists?
var formData = {
firstName: 'Linus',
lastName: 'Van Pelt',
age: 20
};
var tryUppercase = function _intention(value) {
return (typeof value === 'string') ? value.toUpperCase() : value;
}
var mapObject = function (f, object) {
// ???
};
mapObject(tryUppercase, formData);
/* expected:
{
firstName: 'LINUS',
lastName: 'VAN PELT',
age: 20
}
*/
Of course we can.
Using all we made so far
// A naive implementation to use all we have
var mapObject = function (f, object) {
var mappedPairs = toPairs(object) // Step 1: Convert to pairs
.map(function (pair) {
var key = pair[0],
value = pair[1],
newValue = f(value, key); // Step 2: Map over the value
return [key, newValue]; // Can be more succinct
});
return fromPairs(mappedPairs); // Step 3: Convert to object
};
Notice how we transform the object to pairs and back again. This demonstrates the first virtue.
What if we want to find the differences between an old and new object?
var diff = function (oldObject, newObject) {
return mapObject(function (oldValue, key) {
var newValue = newObject[key];
return oldValue !== newValue;
}, oldObject);
}
var oldData = { company: 'Outbreak Comapny', position: 'Ambassador', gender: 'male'},
newData = { company: 'IT Outsourcing', position: 'Ambassador', gender: 'male'};
var diffData = diff(oldData, newData);
console.log(diffData);
/* out:
{ company: true, position: false, gender: false }
*/
if (diffData.company) { // Compare with oldData.company !== newData.company
// Do something when the company changes
}
Let’s move on to filtering collections given a criteria.
Say we have a list of movie titles and we only want the James Bond films.
var films = {
{ name: 'Moonraker', category: 'James Bond'},
{ name: 'Sucker Punch' category: 'Action'},
{ name: 'Casino Royale', category: 'James Bond'}
};
var isJamesBondFilm = function _predicateIntention(film) {
return film.category === 'James Bond';
};
whatFunctionIsThis(isJamesBondFilm, films);
There is a function for lists called filter that can get our job done
Given a predicate or a boolean function and a list, return a new list where the predicate is true over the list.
// Some trivial examples
var numbers = [1, 2, 3, 4, 5];
var isOdd = function (number) {
return number % 2 === 1;
};
var isTheOne = function _heyJetLi(number) {
return number === 1;
};
console.log(numbers.filter(isOdd)); // out: [1,3,5]
console.log(numbers.filter(isTheOne)); // out: [1]
So we have another way to work with lists.
Again, we implement this for our own benefit.
// Another naive
var filter = function (p, xs) {
var ys = [];
forEach(function _checkPredicate(x) {
var y = x; // Just to get the name right with ys
if (p(x)) {
ys.push(y);
}
});
};
var isLowerCase = function (text) {
return text.isLowerCase() === text;
};
var vendors = ['TORGUE', 'Hyperion', 'dahl'];
console.log(filter(isLowerCase, vendors)); // out: ['dahl']
So we have another trick to use with lists. However, this trick has two other concepts attached to it.
Who here likes nested if
statements?
var a === true;
if (a) {
// One if is okay
}
var b === false;
if (!a) {
if (b) {
// Two ifs and you might want to refactor this if you can
} else {
}
}
var c === false;
if (!c) {
if (!b) {
if (!a) {
// Who the hell will write this?
}
}
}
I don’t like branching, it’s too easy to do and easy to make the code more complicated to reason about with too many ifs.
Consider this code as an example of nesting ifs. Given a list of books that are to be processed and a book isbn table, find all the unprocessed books without an isbn
var books = [
{ name: 'The Picture Of Dorian Gray', processed: false},
{ name: 'Minecraft For Dummies', processed: true},
{ name: 'Functional Programming In Javascript', processed: false}
];
var bookIsbnTable = {
'The Picture Of Dorian Gray': '1234',
'Minecraft For Dummies': '2344',
'Skyrim: The Official Guide': '3450'
}
var booksWithoutIsbn = [];
books.forEach(function (book) {
if (!book.isProcessed) {
var isbn = get(book.name, bookIsbnTable);
if (!isbn) {
booksWithoutIsbn.push(book);
}
}
})
Let’s refactor this to get to my point
Separate the main intentions
var books = [ /* ... */];
var bookIsbnTable = { /* ... */ };
// Intentions
var isProcessed = function (book) {
return book.isProcessed === true;
};
var hasNoIsbn = function (book) {
var isbn = get(book.name, bookIsbnTable);
return !isbn;
}
var booksWithoutIsbn = [];
books.forEach(function (book) {
if (isProcessed(book)) {
if (hasIsbn(book)) {
booksWithoutIsbn.push(book);
}
}
})
Let’s remove the first if
statement using the new found filter
power
var books = [ /* ... */];
var bookIsbnTable = { /* ... */ };
// Intentions
var isProcessed = function (book) {
return book.isProcessed === true;
};
var hasNoIsbn = function (book) {
var isbn = get(book.name, bookIsbnTable);
return !isbn;
}
var booksWithoutIsbn = [];
books
.filter(isProcessed) // The if statement becomes a transformation
.forEach(function (book) {
if (hasNoIsbn(book)) { // Just one if left
booksWithoutIsbn.push(book);
}
});
We removed one if, can we remove the other?
We can chain the filter
to remove the other if
var books = [ /* ... */];
var bookIsbnTable = { /* ... */ };
// Intentions
var isProcessed = function (book) {
return book.isProcessed === true;
};
var hasNoIsbn = function (book) {
var isbn = get(book.name, bookIsbnTable);
return !isbn;
}
var booksWithoutIsbn = [];
books
.filter(isProcessed)
.filter(hasNoIsbn) // We not have a filter chain
.forEach(function (book) { // This is somewhat awkward to have
booksWithoutIsbn.push(book);
});
And maybe we can remove the awkward forEach
here
We just let the filter
chain be the result and we’re done
var books = [ /* ... */];
var bookIsbnTable = { /* ... */ };
// Intentions
var isProcessed = function (book) {
return book.isProcessed === true;
};
var hasNoIsbn = function (book) {
var isbn = get(book.name, bookIsbnTable);
return !isbn;
}
var booksWithoutIsbn = books
.filter(isProcessed)
.filter(hasNoIsbn);
Although contrived, we just eliminated the if statements with the
filter
function.
Notice how the code is much more readable and easier to understand without the explicit ifs?
var booksWithoutIsbn = books
.filter(isProcessed)
.filter(hasNoIsbn);
The other thing about this is that the if
is a process flow or a
transformation over the list, not as a branching logic.
Also notice that if someone wants to add another condition, they are more likely to add another filter to the chain and less likely to just hack the if condition.
If objects are also collections, then couldn’t we filter over them as well?
For example, we want to remove all null
or undefined
in an object
so that we can pass it over the wire.
var ajaxData = {
email: '[email protected]',
age: null,
gender: 'male',
birthday: null
}
var isData = function (value) {
return value != null || value != undefined || value != '';
};
console.log(filterObject(isData, ajaxData)); // filterObject???
/* out:
{
email: '[email protected]',
gender: 'male'
}
*/
You already know it is.
I can’t believe this is too easy.
var filterObject = function (p, object) { // Just like mapObject
var filteredPairs = toPairs(object)
.filter(function (pair) { // Notice map is just replaced with filter
var key = pair[0],
value = pair[1],
return p(value, key); // We apply the predicate here
});
return fromPairs(filteredPairs);
}
How about a contrived example?
var request = {
_method: 'POST',
_csrf: 'abcde',
someData: 1
};
var isUnderscored = function (text) {
return text[0] === '_';
};
console.log(filterObject(function (value, key) {
return isUnderscored(key);
}, request));
/* out: {
_method: 'POST',
_csrf: 'abcde'
}
*/
But filtering by keys is not my point.
By thinking about the operations over data, we can abstract the behavior to other containers.
var f = function (x) {
// Do something with x
}
mapX(f, xs); // What is xs? What if xs is a Promise, an Observable
filterX(f, xs); // Sometimes we don't really care
Again the process matters more than the data. If there was another data type the same ideas can come into place.
Finally, we move on to collecting or aggregating all the values of an collection.
For example, given a list of numbers, return their sum
// You're probably getting the picture or getting bored
var numbers = [1, 2, 3, 4, 5];
var sum = 0;
var add = function (a, b) {
return a + b;
};
numbers.forEach(function (number) {
sum = add(sum, number);
});
console.log(sum);
We have the reduce function for this
reduce
takes a combining function and a list and returns the
combined values of the list.
var numbers = [1, 2, 3, 4];
var add = function _theRealIntent(a, b) {
return a + b
};
var sum = numbers.reduce(function _combiningFunction(acc, number) { // Explain this bro
// acc is the accumulated value
// number functions as the number in the list
return acc + number;
}, 0);
// var sum = numbers.reduce(add, 0); // Better, once you understand combining operations
console.log(sum); // out: 10
Let’s implement this like the other two
Once you implement it, the idea of combining function is easy
var reduce = function (oper, initialValue, xs) {
var currentValue = initialValue;
forEach(function combine(x) {
// Combine the currentValue and the next
currentValue = oper(currentValue, x);
});
return totalValue;
};
Let’s have the usual examples
Some basic examples of reduce
var numbers = [1, 2, 3, 4];
var multiply = function (x, y) {
return x * y;
};
console.log(numbers.reduce(multiply, 1)); // out: 1 * 1 * 2 * 3 * 4 = 24
console.log(numbers.reduce(multiply, 5)); // out: 5 * 1 * 2 * 3 * 4 = 120
How about some real examples?
Sometimes in a list, you want to find a specific value given a criteria or predicate.
We can implement this using filter
but we can also implement it
using reduce
.
var find = function (p, defaultValue, xs) {
return xs.reduce(function (prev, next) {
return p(next) === true ? next : prev;
}, defaultValue);
};
var findWithFilter = function (p, defaultValue, xs) {
var foundValues = xs.filter(p);
return foundValues.length > 0 ? foundValues[0] : defaultValue;
};
var isTheMeaningOfLife = function (number) {
return number === 42;
};
console.log(find(isTheMeaningOfLife, 0, [36, 42, 48])); // out: 42
console.log(find(isTheMeaningOfLife, 0, [37, 43, 49])); // out: 0, the default value
Imagine you have an deep object and you want to access an inner object and don’t want to deal with undefined types exception. We can make a safe getter for that.
var deepGetter = function (deepKey, object) {
var keys = deepKey.split('.');
return keys.reduce(function _deepGet(currentObject, key) {
var newObject = get(key, currentObject);
return newObject === null || newObject === undefined ?
undefined : newObject;
}, object);
};
var deepObject = {
a: {
b: {
c: 1
}
}
};
console.log(deepGetter('a.b.c', object)); // out: 1
console.log(deepGetter('a.d', object)); // out: undefined
If you have a list and want an aggregated value, the reduce
should
be a natural choice. It allows you think in terms of combining the
values instead of hacking around at computing it.
We’ll get back to this function later.
Given a list of people, find all the people that are not minors and compute the total.
var people [
{ name: 'Mr Midnight', age: 20, salary: 50},
{ name: 'Mr Muffin', age: 25, salary: 60},
{ name: 'Ms Pepper', age: 17, salary: 40}
];
var isNotAMinor = function (person) {
return person.age >= 18;
}
var getSalary = function (person) {
return person.salary;
}
var add = function (x, y) {
return x + y
}
console.log(people
.filter(isNotAMinor)
.map(getSalary)
.reduce(add)); // Nice to see the three working together
Just a quick review of everything we’ve done.
- Think in terms of input and output
- Separate the intention from the implementation
map
,filter
andreduce
should be your best friends- Process over data
Let’s get to the fun stuff
Spoilers
compose
&pipe
curry
Functions represents behavior or what we have been calling intentions.
Primarily, a function should represent a process and not be tangled with other details
Let’s say we have two buttons with their corresponding handlers.
var buttonHandler = function () {
console.log('This button does something awesome');
}
var otherButtonHandler = function () {
console.log('This button does something way better than the other one');
}
All is well
Now, what if we want to track send information to the server that a
button was clicked? Let’s say we have a sendClick
function that
sends data to the server.
var sendClick = function () { /* whatever */}
var buttonHandler = function () {
sendClick(); // Dirty
console.log('This button does something awesome');
}
var otherButtonHandler = function () {
sendClick(); // What the hell are you doing here?
console.log('This button does something way better than the other one');
}
But this implementation is dirty, copy pasting a method for each handler and it ruins the fact the handler should be concerned with sending logging data or what have you.
What we want is to separate the handler from the logging. What we want is to execute a function before the main one.
Let’s call warm up with this doBefore
function combiner, we will
finally use apply
to invoke the functions.
var doBefore = function (before, main) {
return function _executor(/* args */) { // A function that combines functions
var args = [].slice.call(arguments);
before.apply(null, args); // Execute the function before
return main.apply(null, args); // Execute the main function
}
}
Let’s use this in our example
Let’s see how it makes the code much better
var sendClick = function () { /* whatever */}
var buttonHandler = doBefore(sendClick, function () { // Wrapping the handlers
console.log('This button does something awesome');
}); // This functions returns the combined function
var otherButtonHandler = doBefore(sendClick, function () {
console.log('This button does something way better than the other one');
});
Notice our handlers are just the same except they are now wrapped with the logging function. This is what it means to combine and separate behaviors.
How many times have you copy pasted a block of function to several places without consideration for it’s purpose or intent?
If we cleanly separate the intentions of our code and combine them properly, we can get easy to read and understand code.
What we just did is make an higher order function but that’s just definition.
What if instead of executing before, we prevent the execution of the next one?
For example, if you have a button and you want to allow the event to continue if the user has enough privilege or what have you.
var executeIf = function (checkIf, main) {
return function (/* args */) {
var args = [].slice.call(arguments);
// Check the condition
if (checkIf.apply(null, arguments) === true) {
return main.apply(null, arguments);
}
}
};
var checkWhatever = function (/* args */) { // Check for something
console.log('You shall not pass');
// alert('You shall not pass'); // also works
return false;
};
var buttonHandler = executeIf(checkWhatever, function () {
console.log('Button has been clicked');
});
Again the code to glue the two functions together is cheap and easy.
How about we prevent a button being pressed in a short burst, could be useful for submit buttons
var debounce = function (f, delay) { // I didn't come up with debounce
var blockInvokations = false; // Using closures here, spoilers
return function (/* args */) {
var args = [].slice.call(arguments);
if (blockInvokations === false) {
blockInvokations = true; // Block succeeding invokations
setTimeout(function _unblock() { // Unblock it after the delay
blockInvokations = false;
}, delay);
return f.apply(null, arguments); // Execute normally
} else {
// Can't execute yet, got to wait
return undefined;
}
}
};
var buttonHandler = debound(function _actualHandler() {
console.log('You just paid some website some electronic cash');
}, 300); // 300 is a magic number
A little more complicated but it does show how the we can separate concerns or behaviors
Once you understand that you can join functions together, you can create a pipeline using functions.
Let’s start with an simple problem of uppercasing every text field in an object then converting into JSON.
var isText = function (text) {
return typeof text === 'string';
}
var objectToUpperCase = function (object) {
return mapObject(function (value) {
return isText(value) ? value.toUpperCase();
}, object);
};
var objectToJson = function (object) {
return JSON.parse(object);
};
var data = { name: 'Muffin', breed: 'Puspin', age: 4 };
var output = objectToJson(objectToUpperCase(data)); // The end result
// But I shouldn't care about toJson or toUpperCase
Better if the two functions can be joined as one intention, objectSerialize
?
Just to demonstrate the extra parenthesis are not just dirty, but harder to read and maintain
// Imagine you have three weird functions
var x = function (v) {/* ... */},
y = function (v) {/* ... */},
z = function (v) {/* ... */};
var a = null;
// This is ugly
var b = x(y(z(a)));
var t = function (v) {
return x(y(z(v))); // Better hide the mess like a guy
};
// Better but kinda hacked
var c = t(a);
What we really care about is c
, not x
, y
, or z
. What we need
is something to tie the pipeline.
So the function we’re looking for compose
, given two functions it
will call the second one with the arguments and pass it’s output to
the first function and be the function of the return value.
Easier to explain with code. Remember we want to remove the ugly wrapping of parenthesis in function invokation.
var compose = function (outer, inner) {
return function (/* args */) {
var args = arguments;
var firstValue = inner.apply(null, args);
// First, invoke the inner function
var nextValue = outer.call(null, firstValue);
// Then invoke the outer function with the return value as the argument
return nextValue;
};
},
Let’s see this in our example above
Let’s join those two functions in with compose
var objectToUpperCase = function (object) {
// ...
};
var objectToJson = function (object) {
// ...
};
// Tying the two functions together, looks cleaner than before
var objectSerialize = compose(objectToJson, objectToUpperCase);
// Remember to read from right to left in order of execution
var data = {
// ...
};
console.log(objectSerialize(data));
We successfully removed those wonky parenthesis but how else can compose help us in our journey of better code.
Let’s get acquainted with our new friend, make it your BFF if you can.
// Mathematical Examples
var square = function (n) { return n * n; },
increment = function (n) { return n + 1; },
decrement = function (n) { return n - 1; },
display = function (n) { console.log(n); return n; };
var nextSquare = compose(square, increment),
incrementTwice = compose(increment, increment),
addZero = compose(decrement, increment),
displaySquare = compose(display, square);
Composing is better when you start using it with closures and currying. But for now, how do we compose more than two functions?
So how do we compose functions all the way to the moon?
// Three functions
var escapeHtml = function (text) { /* ... */ },
wrapAsText = function (text) { /* ... */ },
wrapInDiv = function (text) { /* ... */ };
var data = ['5 Centimeters Per Second', 'Garden Of Words'];
// Plural compose, composes.
var htmlizer = composes( // Three compositions
wrapInDiv,
wrapAsText,
escapeHtml); // Do note, that this seems declarative on the process
// Hypothetical scenario in converting a list into Html text
console.log(data
.map(htmlizer)
.join('\n'));
First, we go back to our old friends
Let’s implement a few things to improve the readability of our grand
composes
// Returns an reversed version of the list
var reverse = function (xs) {
var nxs = xs.slice(); // Shortcut for copying an array
nxs.reverse(); // Mutates the array
return nxs;
};
// Returns the first element of the list
var first = function (xs) {
return get(0, xs); // or just xs[0] if you are in a hurry
}
// Likewise the rest of the lements of the list
var rest = function (xs) {
return xs.slice(1);
};
// Just a reduce with the initial value being the first value in the list
var reduceFirst = function (oper, xs) {
var initialValue = first(xs),
otherValues = rest(xs);
return reduce(oper, initialValue, otherValues);
};
I prefer to read left to right instead of right to left with my function pipeline.
var pipe = function (first, next) {
return compose(next, first);
};
var splitWords = function (sentence) { return text.split(''); },
splitParagraphs = function (doc) { return text.split('\n'); };
// Originally
var getWords = compose(splitWords, splitParagraphs);
// Really, notice the sequence is read left to right
var getWords2 = pipe(splitParagraphs, splitWords);
This is just compose
with the arguments reversed which might be a
small thing but helps in readability, just my prefrence anyway.
With that out of the way, we simply use reduceRight
and pipe
in one master stroke
var composes = function (/* fs */) {
var fs = [].slice.call(arguments);
// You can use compose instead of pipe but you would have to reverse the arguments
return reduceFirst(pipe, reverse(fs));
};
That’s how easy it is to implement composes
.
Just for the sake of symmetry.
var pipes = function (/* fs */) {
var fs = [].slice.call(arguments);
return reduceFirst(pipe, fs); // This is just without the reverse
}
Now let’s see how they fit together.
Let’s check it out.
var twice = function (n) { return n * 2; },
half = function (n) { return n / 2; },
increment = function (n) { return n + 1; };
var sequence = composes(half, twice, increment);
var sequence2 = pipes(increment, twice, half, twice);
var sequence3 = pipes( // Preferred way of writing
increment,
twice,
half,
twice
);
Viola, we can compose as much as we want. But where does this leave us?
It’s not the fact that you use compose
or pipe
, but rather that
you want your code to be a single and straight process.
For example, displaying data in html. The process you want is.
- Source
- Map
- Render
But we tend to diverge from this as we add more features. The point is you want to maintain the integrity of the process.
Let’s have another case review. Given a list of employees and there salary, let’s compute their take home pay.
var codeCats = [
{ name: 'Mr. Muffin', salary: 100, gender: 'male'},
{ name: 'Ms. Fuzbudget', salary: 50, gender: 'female'}
];
var payComputer = function (codeCat) {
var pay = codeCat.salary / 2;
return set('pay', pay, codeCat);
};
console.log(codeCats.map(payComputer));
All is fuzzy and warm
Now what if every female employee gets paid 50% more due to a goverment law. What do you do to make it right?
var codeCats = [
{ name: 'Mr. Muffin', salary: 100, gender: 'male'},
{ name: 'Ms. Fuzbudget', salary: 50, gender: 'female'}
];
var payComputer = function (codeCat) {
var pay = codeCat.salary / 2;
var newerPay = codeCat.gender === 'female' ? pay * 1.5 : pay;
return set('pay', newerPay, codeCat);
};
console.log(codeCats.map(payComputer));
Not a problem, but what if you can’t modify payComputer
because
it’s a third party shiznit? Or what if there is another law, are we
going to add another if?
You know where this is going.
Let’s use composition to make the code cleaner.
var codeCats = [
{ name: 'Mr. Muffin', salary: 100, gender: 'male'},
{ name: 'Ms. Fuzbudget', salary: 50, gender: 'female'}
];
var femalePayRaise = function (codeCat) {
var basePay = codeCat.pay; // Must already be paid
return set('pay', codeCat.gender === 'female' ? basePay * 1.5 : basePay, codeCat);
};
var payComputer = compose( // Process is maintained
femalePayRaise,
function _originalComputer(codeCat) {
var pay = codeCat.salary / 2;
return set('pay', newerpay, codeCat);
});
console.log(codeCats.map(payComputer)); // Still well
We retain the original intent and managed complexity.
Although the example is quite contrived, the main point is to avoid complexity and maintain the simplicity of the process.
The thinking style should be a composition pipeline. A straight flow is better than a branching sewer.
As a tip, whenever there is a new functionality or rule, consider composition or the like to manage the complexity.
Currying is a nice functional idea to sweeten composition
Consider this multiplication function, what if we want the first argument to be preset.
var multiply = function (x, y) { return x * y; }
// What if we want have the following?
var multiplyByTwo = function (y) { return multiply(2, y); },
multiplyByFive = function (y) { return multiply(5, y); };
// There is a sort of duplication here. How about?
var multiplyPreset = function (x) { // Neat little trick
return function (y) { // Return a function where the first argument is preset
return multiply(x, y); // Execute the actual function with the arguments
};
};
// Same as above but notice we removed the noise in declaring the functions?
var multiplyWithTwo = multiplyPreset(2),
multiplyWithFive = multiplyPreset(5);
console.log(multiplyWithFive(4)); // out: 20
console.log(multiplyWithTwo(2)); // out: 4
Let’s have another example with a function that takes three arguments
// Consider an function that add triples
var addTriple = function (x, y, z) { return x + y + z; };
// Let's repeat the neat little trick
var addTriplePreset = function (x) {
return function (y) {
return function (z) { // Looks kinda coo
return addTriple(x, y, z);
};
};
};
var addTen = addTriplePreset(10);
console.log(addTen(4, 5)); // out: 19
var addTenAndFive = addTriplePreset(10)(5);
console.log(addTenAndFive(6)); // out: 21
But this seems to be a function behavior, not logic. Maybe we can separate this behavior.
var curry = function (f) { // Thanks Haskell Curry
/* ??? */
};
// Let's curry our get function
// get takes the key and the object
var getId = curry(get)('id'); // or function (object) { return get('id', object); }
var users = [
{ id: 1, username: 'Beethoven'},
{ id: 2, username: 'Mozart'},
{ id: 3, username: 'Ravel'}
];
console.log(users.map(getId)); // out: [1,2,3];
console.log(users.map(function _getId(user) { // Compare with this
return get('id', user);
}));
Let’s implement the easy version
Copy pasting our trick from before
var curry = function (f) {
return function (x) {
return function (y) {
return f.apply(null, [x, y]); // or f(x, y);
}
}
};
This is enough but what if you want to implement it for three arguments or four? You could copy paste the same definitions over and over.
But there is a better way.
This is the most fun and challenging to code but the main concept is to keep returning a function until enough arguments have been supplied and then invoke it.
var curry = function _recursiveCurry(f) {
// Get the number of arguments in a function
var numberOfArgs = f.length;
// An helper function to curry the original function
var currier = function _recursiveCurry(previousArgs) {
return function _curried(/* currentArgs */) {
var currentArgs = [].slice.call(arguments),
newArgs = previousArgs.concat(currentArgs);
// If there is enough args, invoke the function
if (newArgs.length >= numberOfArgs) {
return f.apply(null, arguments);
} else { // If there is not enough args yet, keep currying
return _recursiveCurry(newArgs);
}
};
};
return currier([]); // Start recursion with no arguments
};
This is the only thing that looks complicated, I swear.
Just a contrived use of the generic curry
var computeName = curry(function _fullName(firstName, lastName) {
return firstName + ' ' + lastName;
});
var doctorName = computeName('Doctor');
console.log(doctorName('Death')); // out: Doctor Death
console.log(doctorName('Who')); // out: Doctor Who
var johnName = computeName('John');
console.log(johnName('Marshton')); // out: John Marshton
console.log(johnName('Doe')); // out: John Doe
// Cute tricks with it, but don't do this
console.log(computeName('Linus')()('Van Pelt')); // out: Linus Van Pelt
console.log(computeName()()()('Linus', 'Van Pelt')); // out: Linus Van Pelt
But where does this fit in with compose
?
Curry a function until it has one argument left which you can pass through a composition or mapping pipeline.
var heroes = [
{ name: 'Yang Xiao Long', team: 'RWBY'},
{ name: 'Ruby Rose', team: 'RWBY' },
{ name: 'Pyrrha Nikos', team: 'JNPR'}
];
var setHealth = curry(set)('hp', 100),
setScore = curry(set)('score', 0);
console.log(heroes
.map(setHealth)
.map(setScore));
// or if you care about performance
var setStats = compose(setHealth, setScore); // or pipe(setScore, setHealth);
console.log(heroes.map(setStats));
// Did I declare a function??
Doesn’t it look nice? We just created new functions from old ones and given them more specific uses.
How about using it in conjuction with filter? Given a list of heroes, I want to filter the intelligence type heroes in the list or team.
var heroes = [
{ name: 'Pudge', type: 'STR', kills: 15},
{ name: 'Puck', type: 'INT', kills: 13},
{ name: 'Lich King', type: 'INT', kills: 9}
{ name: 'Razor', type: 'AGI', kills: 4},
{ name: 'Ogre Magi', type: 'STR', kills: 1}
];
var eq = curry(function _equals(a, b) {
return a === b;
});
var getType = curry(get)('type'),
isInt = eq('INT');
var isIntType = pipe(getType, isInt);
console.log(heroes.filter(isIntType));
// compare with, I leave it to you which is better
console.log(heroes.filter(function _isIntType(hero) {
return heroes.type === 'INT';
}));
Still cool
Let’s say you have a third party component componentBuilder
and
the a web page has a whole lot of it. You can curry the builder with
the options required and let it be the function that builds it to
serve as a function interface.
// The third party builder function
var componentBuilder = function (options, element) {
/* ... */
};
// The options for the page
var pageOptions = {
/* ... */
}
// I don't care about the options, just build it when I give it to you
var buildComponent = curry(componentBuilder)(pageOptions);
var domComponents = [
document.getElementById('period'),
document.getElementById('type'),
document.getElementById('projects')
];
// Just build it for each of this
domComponents.forEach(buildComponent);
I love currying functions that all my functions in my module should be curried by default.
var isTypeOf = curry(function (type, value) {
return typeof value === type;
});
var isFunction = isTypeOf('function');
var invokeIf = curry(function (pred, f, value) {
return pred(value) ? f(value) : value;
});
var autoCurry = curry(mapObject)(invokeIf(isFunction, curry));
var Module = autoCurry({
doThis: function (a, b) { console.log([a, b]); }
});
Module.doThis(1)('b'); // out: [1, 'b']
With higher ordered concepts like composition and currying, if everything is a function then all of them benefit from higher order abstractions.
Now you know why get
and set
are functions so that they can be
curried and composed.
There are other higher order functions that sweeten the deal.
So those are my fundamental tricks.
compose
,pipe
andcurry
are fun tools to implement and have- Combining functions are easy
- Making new functions from old are easy
This last section is much more concerned with the philosophy of this paradigm
Spoilers
- Closures
- Immediately Invoked Function Expressions(IIFE)
Does anyone know the output of this snippet?
var games = ['Tetris', 'Pong', 'Mortal Kombat'];
// Sort the list
var sortedGames = games.sort());
console.log(sortedGames);
The sorted list but there is something else afoot here.
console.log(games === games.sort());
This returns true. The list is sorted in place, so they’re the same. This is what is known as a side effect.
This is simply something that changes when the function is invoked. This is also known as state mutation.
var counter = 0;
var sum = function (numbers) {
counter += 1; // State mutation or side effect
return reduceFirst(function (a, b) { return a + b; }, numbers);
};
console.log(counter); // out: 0
// We didn't pass in counter...
console.log(sum([1,2,3])); // out: 6
// ... yet why is it changed without my conset
console.log(counter); // out: 1
How do you know if a function has side effects? How do you know if the parameters aren’t manipulated or changed without your consent?
The main point is that it is harder to reason about code or understand when it changes something outside it’s scope or function.
Side effects aren’t bad, we just want to minimize it if we can.
Functions should (almost) never do the following
- Mutate the parameters
- Mutate global state
- If you do mutate, make it explicit or known
- If you do need state, we have closures for that
This is the hardest to follow. If you notice yourself making state or mutating variables, consider the consequences..
What closures really are is that they remember the environment, scope or variables.
var rememberThis = function (value) {
var rememberMe = value; // This value will exist until function does
return function () {
return rememberMe; // It remembers me
};
};
var rememberTheDate = rememberThis('Fifth Of November');
// Several bad puns later
console.log(rememberTheDate()); // out: Fifth Of November
Here is the fun bit: can you change the value of rememberMe
directly?
Closures are really state for functions.
var incrementer = function (startValue) {
var currentValue = startValue; // Function state
return function () {
// The value is increased each time it is called
currentValue += 1; // Mutate the state
return currentValue;
};
};
var increasingSequence = incrementer(0);
console.log(increasingSequence()); // out: 1
console.log(increasingSequence()); // out: 2
console.log(increasingSequence()); // out: 3
This is a form of state mutation that is well contained within the
function. Nobody can access currentValue
directly.
You can create lazy sequences with this technique.
Closures can provide what is known as private variables to OOP
// Creating an entity with function closure
var person = function (initialName, initialGender) {
var currentName = initialName,
currentGender = initialGender;
// This time we return an object that interacts with the closure
return {
getName: function () { return currentName; },
getGender: function () { return currentGender; },
// Prefer to make a new copy but to show state here
setName: function (newName) { currentName = newName; },
// There is no change gender function
};
};
var me = function ('FnMurillo', '♂');
console.log(me.getName()); // out: FnMurillo
console.log(me.getGender()); // out: ♂
// Who I really am
me.setName('Batman');
// It changed!!
console.log(me.getName()); // out: Batman
Although we can implement private variables with closures, what we want is to hide it as much as possible. The closure should be the only one to modify it state, there’s really no point in hiding it in a closure if you are going to reveal it anyway, an object would do the job instead.
This is the opposite of data encapsulation.
Let’s talk about a small definition to where closure applies easily
Fancy words saying functions that always give the same output for the same input.
var twice = function (n) { return n * 2; }; // Math, very idempotent
// Weird function that adds whatever you put in last with the current
var addPrevious = function () {
var previous = 0;
return function (n) {
oldPrevious = previous; // Store old value
previous = n; // Mutate value
return oldPrevious + n; // Do the math;
}
}
var adder = addPrevious();
console.log(adder(5)); // out: 5 // oldPrevious = 0, newPrevious = 5
// Does not return the same value
console.log(adder(5)); // out: 10 // oldPrevious = 5, newPrevious = 10
var hiddenState = 0,
decrement = function (n) {
hiddenState += 1;
return n - 1;
};
// This has side effects but it is idempotent
console.log(decrement(4)); // out: 4 // hiddenState = 1
console.log(decrement(4)); // out: 4 // hiddenState = 2
Just another short demonstration using closure. If a function is idempotent, you can cache the values. Caching for functions basically
// Memoization for one argument function
var memoize = function (f) {
var cacheTable = {}; // Closure state, note that this hidden all throughout
return function (n) { // Implemented for only one argument
var cachedValue = get(n, cacheTable); // Get cached value
if (cachedValue === undefined) { // If there is no cached value
cachedValue = f.call(null, n); // Invoke the function
set(n, cachedValue, cacheTable); // Store the value
}
return cachedValue;
}
};
var encrypt = memoize(function (text) {
var veryLongEncryptionAlgorithm = function _sha1Maybe(text) { /* ... */};
return veryLongEncryptionAlgorithm(text);
});
Again, this is just to show that closures are supposed to hide state.
There is a nice feature or trick in JavaScript known as Immediately Invoked Function Expression or IIFE for short.
This just creating a function and invoking immediately.
var y = (function _iife() { // Just create a function
return 1; // Why do this?
}()); // ... and wrap it with an open close parenthesis
var z = 1; // Why not just do this instead?
This is cute, but what is is good for? Since it is a function, it has closure; so anything you put inside it, remains inside.
When creating scripts for JavaScript, it’s good to wrap the whole script as an IIFE to prevent and variable leaks since the default scope of variables is global.
(function _importMyCustomScript() {
var myScriptVar = 1;
// Assume the global namespace for my app is XYZ
XYZ.myFunction = function () { return myScriptVar; };
}());
This goes hand in hand with creating a global namespace if you are working with the browser but we’re not really going to dwell into that.
You can also create a module using IIFE with this pattern.
// Using IIFE to make modules
var FP = (function () {
var hiddenVarInModule = 1;
// Object.create allows us to create truly empty object
var Module = Object.create(null); // or {} is enough if you don't care
// Object.freeze makes an object readonly
return Object.freeze(Module); // or Module if you don't care
// Return the module
}());
Not the best use, but when you are trying to sketch a function and can’t decide if it can be refactored. Starting with IIFE can get you started.
var originalFunction = function () {
// Blah blah blah
// New feature, need a better name for this function
(function _newFeature() { // If this is used again, it is easier to refactor
// Everything I do is within this bubble
var x = 1,
y= 'a';
// Do more stuff
}())
// Blah blah blah
};
I usually sketch the prototype within an IIFE, and keep it there to give it a good indication of what it does. Then if it used again, I just turn into an legit function and refactor.
Hiding state.
This is what closures should ultimately do. But what if you had to? Here are some examples.
JavaScript has blessed syntax for object creation: easy and flexible to use.
var myObject = {
myText: '',
myValue: null,
mySubObject: {
isEmpty: true
},
};
// It's actuall okay to mutate
// NOTE: A comment to say that it mutates
var newObject = set('myText', 'xyz', myObject);
console.log(myObject.myText); // out: 'xyz'
// but
console.log(newObject === myObject); // out: true
I put an object in, I get an object out. I don’t care if you mutated it, but be very mindful or explicit about this. Comments are your friends. Using objects as a data format is a solid way to go about it.
One of your friends too in this field is known as Immutability which might be a different topic.
What if given a big list of numbers, compute the average; what if you could loop through it once? How would you do it?
var bigDoop = [1, 2, 3, /* ... */, 10000000]; // Just a very big set
// What appears to be more cumbersome
var computeAverage = function (numbers) {
var getSum = curry(get)(0),
getCount = curry(get)(1);
var totals = numbers.reduce(function (prev, number) {
var currentSum = getSum(prev),
currentCount = getCount(prev);
return [currentSum + number, currentCount + 1];
}, [0, 0]); // Setting the initial state
var sum = get(0, totals),
count = get(1, totals);
return sum / count;
};
console.log(computeAverage(bigDoop)); // out: More numbers than Iron Man
Notice the use of a lists to pass in state, the count and the sum. If you’re in a pipeline, it is much better to make the state explicit as a value for all to see and inspect.
This is not really practical but just to get your attention. Let’s implement a fancy recursive factorial implementation.
// Avoid doing this, this is not helpful
var factorial = function (n) {
var factorialHelper = function _recurse(n, value) {
if (n <= 0) { // If done, return the value
return value;
} else { // Else, do it again with the new state
return _recurse(n - 1, value * n); // This is unhealthy to pass state
}
};
return factorialHelper(n, 1); // Call with initial state
};
var factorialWhile = function (n) {
var value = 1;
while (n > 0) {
value *= n;
n-= 1;
}
return value;
}
If JavaScript was tail optimized, we would really do more of this but it scares me to do it. But this is just to show the weird way to pass in state.
In reality, hiding state is hard.
Being explicit about state almost translates to more code; this is what can put you off to declare that global variable or just hack the state variables.
I suggest paying the explicit code than someone paying for it implicitly.
Despite all that rant about state, if JavaScript didn’t have closure, I wouldn’t know what to do about my work and there really wouldn’t be a functional paradigm for it.
Imagine if you could access all the variables inside a function?
Have I made my point?
- Closures are underrated in managing state
- State makes code more complex, hide if you must
- Objects and lists make excellent function data interchange
End of the line.
JavaScript is really cool for me because of the following.
- First class functions
- Closures
- Function scoped
That’s really it.
(I could say the same thing about Python which holds a special place in my heart.)
If you forgot what I said, here it is.
- Use
map
,filter
, andreduce
- Learn
compose
andcurry
- Think input and output, processes not details
- Avoid for loops and ifs
- Hide implementation details
- Hide state as much as possible
- Have fun
Compare JavaScript with Haskell if you want to see the light
- Types
- Monads
- Functors
- Patterns
- So much
I am not an Oracle but…
- Functional Reactive Programming
- React & React Native
- Clojure / ClojureScript
- RxJs
- Elm
- Java 8: Lambdas
Really, it’s just a good time to learn a new paradigm to widen your perspective.
No matter what I say or what paradigm is out there. Good code is something you work for. There is really no silver bullet.
Don’t be a zealot. OOP and FP have their places in design and coding. Use your judgment which works for the correct situation.
But really in JavaScript, I find FP to be really easy to work with than its Prototypical nature.
Thank you for letting me waste your time. I hope you learned something or even just the idea.
- lodash - a good functional library
- jquery - not obvious but it really is a monad
- rxjs - functional reactive programming library
- haskell - the language drug to functional programming
- clojure - the functional lisp since Scheme
- clojurescript - the lisp for javascript
- elm - the functional javascript