Talk Functional Javascript!
//+ CV.js :: September 18th, 2013
Kevin Welcher
Shout Out
Shout Out
Something about Camp?
10k Feet
• [Underscore.js / Lodash.js]
• Functional Concepts
• Composition
• Currying
• [Demo]
Underscore.jsLodash.js
Highlights
• reduce
• map
• pluck
• filter
• find
reduce
• The grandaddy, most iterators can be made out of reduce
• “Reduce a list to a new object”
map
• Bread and butter iterator
• Used to transform a list of elements from one type to another
• Whatever is returned within the iterator is the new object
pluck
• Iterates over a list of objects and returns a new list made of the specified property from each object
• A common use for map
• IE: plucking the name from an array of people objects
filter
• Only allows values that pass a predicate into the new list
• As with all these methods, it returns copies of the data
find
• Accepts a predicate and returns the first item that passes the predicate
Much, much more...• Many predicates
(isArray, isNull, ...)
• keys / values
• groupBy
• result
• array methods
• union / intersection
• chain / compose
• extend / defaults
• simple templates
• pick / omit
FP vs OO
• The language of the... language
• How complexity is hidden
• How data is stored
Functional Concepts
Function purity
• Deterministic
• Does not depend on external state
• Does not depend on IO
• Does not cause side effects
Function purityvar View = Backbone.View.extend({ initialize: function() { this.generateList(); }, generateList: function() { this.list = new Backbone.Collection([ {name: 'sally'}, {name: 'joe'} ]); }});
Function purityvar View = Backbone.View.extend({ initialize: function() { this.list = this.generateList(); }, generateList: function() { return new Backbone.Collection([ {name: 'sally'}, {name: 'joe'} ]); }});
Functions as data
• First class citizens
• Identified by their returns
• Modifiable
Functions as building blocks
Functions which consume the return value of the function that follows.
Composition:
Functions as building blocks
a() b() c()
a(b(c(x))) === compose(a, b, c)(x)
Composition
Functions as building blocks
function add1 (x) { return x + 1;}
var add3 = compose(add1, add1, add1);
add3(0);
//> 3
Functions as building blocks
function add1 (x) { return x + 1;}
var add3 = compose(add1, add1, add1);
add3(0);
//> 3
0
Functions as building blocks
function add1 (x) { return x + 1;}
var add3 = compose(add1, add1, add1);
add3(0);
//> 3
01
Functions as building blocks
function add1 (x) { return x + 1;}
var add3 = compose(add1, add1, add1);
add3(0);
//> 3
012
Functions as building blocks
function add1 (x) { return x + 1;}
var add3 = compose(add1, add1, add1);
add3(0);
//> 3
0123
Functions as building blocks
function not (x) { return !x;}
var boolify = compose(not, not);
not(not({}));boolify({});
//> true
Functions as building blocks
function addTitle (x) { return ‘Mr. ‘ + x;}
function addSalutation (x) { return ‘G’day, ‘ + x;}
var meetAndGreet = compose(addSalutation, addTitle);
addSalutation(addTitle('Baggins'));meetAndGreet('Baggins');
//> G’day, Mr. Baggins
Functions as building blocks
function add (x, y) { return x + y;}
var add1 = compose(???);
//> profit
Currying
“Taking a function that takes multiple arguments and transforming it to a chain of functions that accept a single
argument.” - Wikipedia
Notation
//+ <method name> :: type -> type -> type
Notation
//+ <method name> :: type -> type -> type
A function that take takes an argument and returns a value of type
Notation
//+ <method name> :: type -> type -> type
A function that take takes an argument and returns a value of type
integer -> integer
A function that takes in a single argument of type integer
The return type of integer. Since nothing follows, it is the final return
Notation
//+ <method name> :: type -> type -> type
A function that take takes an argument and returns a value of type
Notation
//+ <method name> :: type -> type -> type
The ultimate return value of a function
//+ not :: a -> boolean
function not (x) { return !x;}
not(true) //> false
//+ not :: a -> boolean
function not (x) { return !x;}
not(true) //> false
The a (or any other lower case letter) means we don’t care about the type.
//+ add :: a, a -> a function add(x, y) { return x + y;}
add(1, 2); //> 3
//+ add :: a, a -> a function add(x, y) { return x + y;}
add(1, 2); //> 3
I am abusing notation :(
“Taking a function that takes multiple arguments and transforming it to a chain of functions that accept a single
argument.” - Wikipedia
//+ add :: a, a -> a
//+ add :: a -> a -> a
//+ add :: a -> a -> a
function add(x) { return function (y) { return x + y; };}
add(1)(2); //> 3
ed
//+ add :: a -> a -> a
function add(x) { return function (y) { return x + y; };}
add(1)(2); //> 3
ed
//+ add :: a -> a -> a
//+ add :: 1 -> a -> a
//+ add1 :: integer -> integervar add1 = add(1);
add1(2); //> 3
//+ add :: a -> a -> a
//+ add :: ‘Mr. ‘ -> a -> a //+ addTitle :: string -> stringvar addTitle = add('Mr. ');
addTitle('Baggins');//> Mr. Baggins
Problem
That _ is ugly
//+ add :: a -> a -> a
function add(x) { return function (y) { return x + y; };}
add(1)(2); //> 3
autoCurry
A custom function that automatically curries for you.
For each parameter of a function, it returns a new function.
That _ is ugly
//+ add :: a -> a -> a
var add = function(x, y) { return x + y;}.autoCurry();
add(1, 2); //> 3add(1)(2); //> 3 Awww yissss
Cool Story Bru...
Let Me Convince You With...
More Examples!
reduce
//+ reduce :: fn -> a -> array -> avar reduce = function (fn, start, list) { // ... }.autoCurry();
reduce
//+ reduce :: fn -> a -> array -> avar reduce = function (fn, start, list) { // ... }.autoCurry();
function (cnrt, val, index, list) { // ...}
Sum all the numbers in a list?
var items = [1, 2, 3];
//+ reduce :: fn -> a -> array -> areduce(function (crnt, val, index, list) { return crnt + val;}, 0, items);
//> 6
var items = [1, 2, 3];
//+ reduce :: fn -> a -> array -> areduce(function (crnt, val, index, list) { return crnt + val;}, 0, items);
//> 6
We don’t use these two arguments so we can omit them
var items = [1, 2, 3];
//+ reduce :: fn -> a -> array -> areduce(function (crnt, val) { return crnt + val;}, 0, items);
//> 6
var items = [1, 2, 3];
//+ reduce :: fn -> a -> array -> areduce(function (crnt, val) { return crnt + val;}, 0, items);
//> 6
This function looks familiar...
//+ add :: a -> a -> a
var add = function(x, y) { return x + y;}.autoCurry();
var items = [1, 2, 3];
//+ reduce :: fn -> a -> array -> areduce(function (x, y) { return x + y;}, 0, items);
//> 6
var items = [1, 2, 3];
//+ reduce :: fn -> a -> array -> areduce(function (x, y) { return x + y;}, 0, items);
//> 6 Instead of inlining the function, just pass
the function’s pointer
var items = [1, 2, 3];
//+ reduce :: fn -> a -> array -> areduce(add, 0, items);
//> 6
var items = [1, 2, 3];
//+ reduce :: fn -> a -> array -> areduce(add, 0, items);
//> 6
Cool! But isn’t reduce curried? Can we make this into a generic function?
var items = [1, 2, 3];
//+ reduce :: add -> 0 -> array -> a//+ sum :: array -> integervar sum = reduce(add, 0);
sum(items);//> 6
Demo
https://gist.github.com/kaw2k/6312261
Lessons Learned?
Keep your methods short and to the point
It is easier to build things if your building blocks are small
Stick your data as the last parameter
reduce = function (fn, starting value, data)
function (general, ..., specific)
Optional parameters are hard
Make a general function and curry it to take optional stuff
Functional purity is pretty cool
• Easier to maintain
• Easier to reason
• Easier to test
• Easier to transport
Obligatory Marketing
(We are hiring)