Functional influences Bran van der Meer, 18-08-2015
FunctionalinfluencesBran van der Meer, 18-08-2015
Agenda1. Abstraction
2. Currying3. Composition
Abstraction1
Pure function• No other dependencies than it’s
signature• Referentially Transparent• Stateless• Separated from it’s environment• Memoizable (cachable results)• More testable, portable and
parallelizable
Pure functionvar sum = function(left, right) { return left + right;};
sum(1, 2); //=> 3sum(1, 2); //=> 3sum(10, 2); //=> 12
Impure functionvar sumish = function(left, right) { return (left + right) * Math.random();};
sumish(1, 2); //=> ?sumish(3, 5); //=> ?
Impure functionvar Counter = function() { this._c = 0;};Counter.prototype.increment = function() { return ++this._c;};
var c = new Counter;c.increment(); //=> 1c.increment(); //=> 2
Impure functionfunction daysThisMonth() { var date = new Date(); var y = date.getFullYear(); var m = date.getMonth(); var start = new Date(y, m, 1); var end = new Date(y, m + 1, 1); return (end - start) / (1000 * 60 * 60 * 24);}
Pure functionfunction daysInMonth(y, m) { var start = new Date(y, m, 1); var end = new Date(y, m + 1, 1); return (end - start) / (1000 * 60 * 60 * 24);}
Higher-order function• A function that takes another function
as an argument• A function that returns a function• Famous examples: map, filter, reduce
Higher-order functionvar map = function(fn, arr) { var newArr = []; for (var i = 0; i < arr.length; i++) { newArr.push(fn(arr[i])); } return newArr;}
Higher-order functionvar makeAdd = function(left) { return function(right) { return left + right; };};
var add5 = makeAdd(5);add5(10); //=> 15
Currying2
Currying• Postponing giving arguments to a
function• A curried function can take less
arguments than it needs, it’ll wait for the others
• A curried function keeps returning a function when called, until it has all arguments
• Partial application is calling a function with less arguments than it needs
• In the extreme: giving one argument at a time
Manual curryingvar sum = function(left) { return function(right) { return left + right; };};
var sum5 = sum(5);//=> function(right) { return 5 + right }
sum5(7); //=> 12
One argument at a timevar sum = function(left, right) { return left + right;};
sum(5, 8); //=> 13
sum = curry(sum);
var sum5 = sum(5); //=> function() {...}sum5(7); //=> 12
One argument at a timesum = curry(sum);
sum(1, 2, 3) //=> 6
sum(1)(2)(3) //=> 6
Partial applicationsum = curry(sum);
sum(1, 2, 3) //=> 6sum(1)(2)(3) //=> 6sum(1)(2, 3) //=> 6sum(1, 2)(3) //=> 6sum(1, 2, 3) //=> 6
Useful examplevar getProp = function(prop, obj) { return obj[prop];};getProp = curry(getProp);
var people = [ {name: ...}, ... ];
var names = map(getProp('name'), people);
Underscore & lodashvar getProp = _.curry(function(obj, prop) { return obj[prop];});
var names = map(getProp('name'), people); // ?!
A functional libraryvar split = curry(function(pattern, str) { return str.split(pattern);});var filter = curry(function(fn, arr) { return arr.filter(fn);});var test = curry(function(re, str) { return re.test(str);});
Truly functional: Ramda
Another examplevar words = function(str) { return split(' ', str);};
var words = split(' ');
Yet another examplevar filterQs = function(xs) { return filter(function(x) { return test(/q/i, x); }, xs);};
var filterQs = filter(test(/q/i));
Composition3
Composition• Chaining functions to create a new
function• Passing return values from one to the
other• Composition enables pipelining of data
Naive implementationvar compose = function(f, g) { return function(x) { return f(g(x)); };};
Examplevar shout = function(x) { return exclaim(toUpper(x));};
var shout = compose(exclaim, toUpper);
shout('send in the clowns');//=> "SEND IN THE CLOWNS!"
Not referencing data argumentsmap(function(num) { return Math.sqrt(num);}, numbers);
map(Math.sqrt, numbers); // No arguments
Pointfreevar invert = function(x) { return -1 * x; };var negate = compose(invert, Math.abs);var negateAll = map(negate);
negateAll([-1, -2, 0, 2, -3, 4]);//=> [-1, -2, -0, -2, -3, -4]
Another examplevar cars = [ {price: '48000'}, ...];
var averagePrice = compose( average, map(parseInt), map(getProp('price')));
averagePrice(cars); //=> 42000
More? Training!4