Gleb Bahmutov PRO
JavaScript ninja, image processing expert, software quality fanatic
source: Improved Minesweeper
source: careers.stackoverflow.com
source: modulecounts.com
Changing how JS developers program will have a huge impact on the software quality in the world
Kensho - instant financial analysis
Harvard Sq, Cambridge MA
WTC New York
Kensho - instant financial analytics
number of engineers
lifetime of the company
2 major parts
Everyone has their own path to JS
! junior vs senior
In theory
anyone
functional reactive programmer
In practice
anyone
functional reactive programmer
...
...
...
...
...
...
...
Problem: given an array of numbers, multiply each number by a constant and print the result.
var numbers = [3, 1, 7];
var constant = 2;
// 6 2 14
var numbers = [3, 1, 7];
var constant = 2;
var k = 0;
for(k = 0; k < numbers.length; k += 1) {
console.log(numbers[k] * constant);
}
// 6 2 14
"for" loop is a pain to debug and use
mixing data manipulation with printing
code is hard to reuse
var numbers = [3, 1, 7];
var constant = 2;
reusable simple functions
complex logic
iteration
function mul(a, b) {
return a * b;
}
function print(x) {
console.log(x);
}
function processNumber(n) {
print(mul(n, constant));
}
for(k = 0; k < numbers.length; k += 1) {
processNumber(numbers[k]);
}
// 6 2 14
Boring code does not surprise you
// using "this" WILL surprise you
numbers.forEach(function (x) {
this.doSomething(x);
// ERROR!
});
var self = this;
numbers.forEach(function (x) {
self.doSomething(x);
});
Boring code does not surprise you
// using "this" in your objects
// WILL surprise someone else
numbers.forEach(console.log);
numbers.forEach(console.log.bind(console));
// works under Node
// in Chrome
// Uncaught TypeError: Illegal invocation
Boring code does not surprise you
// for loops WILL surprise you
for(k = 0; k < numbers.length; k += 1) {
processNumber(numbers[k]);
}
common problems: off by 1 errors, iterator variable, changing array size
Boring code does not surprise you
// using non-local variables WILL surprise you
constant = 2;
...
numbers.forEach(function process(n) {
return mul(n, constant);
});
function mul(a, b) {
return a * b;
}
function print(n) {
console.log(n);
}
not really
pure but
close enough for now
var mulBy2 = mul.bind(null, 2);
mulBy2(5); // 10
function mul(a, b) {
return a * b;
}
var mul2by3 = mul.bind(null, 2, 3);
mul2by3(); // 6
function mul(a, b) {
return a * b;
}
var by2 = mul.bind(null, 2);
// same as
var _ = require('lodash');
var by2 = _.partial(mul, 2);
Boring code is nice, but I want encapsulated logic, data access, inheritance!
function NumberMultiplier() {}
Object-Oriented JavaScript
NumberMultiplier.prototype.setNumbers = function (numbers) {
this.numbers = numbers;
return this;
};
NumberMultiplier.prototype.multiply = function (constant) {
for (var k = 0; k < this.numbers.length; k += 1) {
this.numbers[k] = constant * this.numbers[k];
}
return this;
};
NumberMultiplier.prototype.print = function () {
console.log(this.numbers);
};
Object-Oriented JavaScript
// using NEW keyword
new NumberMultiplier()
.setNumbers(numbers)
.multiply(constant)
.print();
// [ 6, 2, 14 ]
Object-Oriented JavaScript: 3 parts
function NumberMultiplier() {}
NumberMultiplier.prototype.setNumbers = function() {}
NumberMultiplier.prototype.multiply = function() {}
NumberMultiplier.prototype.print = function() {}
new NumberMultiplier();
constructor function
prototype
"new" keyword
// NumberMultiplier.prototype object
{
setNumbers: function () { ... },
multiply: function () { ... },
print: function () { ... }
}
OO JavaScript is prototypical
// instance object
{
numbers: [3, 1, 7]
}
prototype
var NumberMultiplier = {
setNumbers: function (numbers) {
this.numbers = numbers; return this;
},
multiply: function (constant) {
for (var k = 0; k < this.numbers.length; k += 1) {
this.numbers[k] = constant * this.numbers[k];
} return this;
},
print: function () { console.log(this.numbers); }
};
Prototype is a plain object!
var numberMultiplier = Object.create(NumberMultiplier);
numberMultiplier
.setNumbers(numbers)
.multiply(constant)
.print();
// [ 6, 2, 14 ]
plain object
class NumberMultiplier {
setNumbers(numbers) {
this.numbers = numbers;
return this;
}
multiply(constant) {
for (var k = 0; k < this.numbers.length; k += 1) {
this.numbers[k] = constant * this.numbers[k];
}
return this;
}
print() {
console.log(this.numbers);
return this;
}
}
new NumberMultiplier()
ES6 classes: just don't
typeof [1, 2] // "object"
Array.isArray([1, 2]) // true
Array.prototype
/*
map
forEach
filter
reduce
...
*/
Arrays: object-oriented + functional
var numbers = [3, 1, 7];
var constant = 2;
function mul(a, b) {
return a * b;
}
function print(n) {
console.log(n);
}
var mulBy = mul.bind(null, constant);
clear "multiply then print" semantics
functional bits
Object-oriented methods
numbers
.map(mulBy)
.forEach(print);
// 6 2 14
// OO (built-in)
Array.prototype.map(cb)
// functional (user space)
Library.map(array, cb)
// plus
Library.groupBy(array, cb);
Library.dropWhile(array, cb);
...
var _ = require('lodash')
var byConstant = _.partial(mul, constant)
Work with arrays using Lodash
var multiplied = _.map(numbers, byConstant)
_.forEach(multiplied, print)
// 6 2 14
_({ John: 3, Jim: 10 })
.map(byConstant)
.forEach(print);
// 6 20
var mulBy = ...
// OO (built-in)
Array.prototype.map(mulBy)
// lodash / underscore
_.map(array, mulBy)
// Ramda
R.map(mulBy, array)
// Ramda
R.map(mulBy, array)
var R = require('ramda');
var byConstant = R.partial(R.mul, constant);
// R.forEach = function (callback, array)
var printEach = R.partial(R.forEach, print);
// R.map: function (callback, array)
var mapByConstant = R.partial(R.map, byConstant);
printEach(mapByConstant(numbers));
// 6 2 14
printEach(mapByConstant(numbers));
// f ( g ( x ) )
var algorithm = R.compose(printEach, mapByConstant);
algorithm(numbers);
// 6 2 14
// fn( data )
algorithm(numbers);
"fn(data)" code is simple to reason about and test
// fn( data )
// R.mul = function (a, b) { ... }
var byConstant = R.partial(R.mul, constant);
Partial application by the client
// in Ramda this is same as simply
var byConstant = R.mul(constant);
// for your functions
var mul = R.curry(function (a, b) {
return a * b;
});
mul(5)(4); // 20
Curry: partial application built-in
var byConstant = R.mul(constant);
R.map(R.mul(constant)) (... data ...)
Ramda: library with all functions curried
var R = require('ramda');
R.pipe(
R.map(R.multiply(constant)),
R.forEach(print)
)(numbers);
// 6 2 14
Curry: program with very little code
Requires careful function signature design
i.e. put information most likely to be known early as first argument
"Smart" objects
Pass data around
"Dumb" objects
"Smart" pipeline
Pass code (functions) around
new NumberMultiplier()
.add(3, 1, 7)
.multiply(2)
.print();
_.forEach(
_.map(numbers, byConstant),
print
);
It is better to have 100 functions operate on one data structure than to have 10 functions operate on 10 data structures."
- Alan J. Perlis
new NumberMultiplier()
.add(3, 1, 7)
.multiply(2)
.print();
_.forEach(
_.map(numbers, byConstant),
print
);
Source of complexity: mutable data
var numbers = [...];
numbers.forEach(processNumber);
// is the list "numbers" unchanged?
function processNumber(x, k, array) {
array[1] = 10000;
print(x);
}
Mutable data makes code hard to understand and modify
Immutable data structures
'use strict';
var immutable = require('seamless-immutable');
var byConstant = _.partial(mul, constant);
immutable(numbers)
.map(byConstant)
.forEach(function (x, k, array) {
array[1] = 10000; // throws an Error
print(x);
});
Efficient immutable JS libraries: seamless-immutable, mori
Immutable data with Redux pattern
function mul(a, b) { return a * b; }
function multiply(state, constant) {
var byConstant = mul.bind(null, constant);
return state.updateIn(['numbers'], function (ns) {
return ns.map(byConstant);
});
}
var immutable = require('immutable');
var initialState = immutable.Map({
numbers: immutable.List.of([3, 1, 7])
});
var newState = multiply(initialState, constant);
// newState !== initialState
// Does NOT work
numbers.map(function (x, done) {
mul(x, function cb(result) {
done(result);
})
})
numbers
.map(asyncFn)
.forEach(asyncFn);
var Q = require('q');
Q(...)
.then(function onSuccess(result) { ... },
function onFailure(error) { ... })
.then(...);
A single async action with a single result or error
var Q = require('q');
function foo() {
var defer = Q.defer();
...
defer.resolve(returnValue);
...
return defer.promise;
}
foo()
.then(bar)
.then(baz);
// functions foo, bar, baz return a promise
Each step in the promise chain can be asynchronous and return a promise!
var sleepSecond = _.partial(Q.delay, 1000);
var pauseMulAndPrint = function (n) {
return function () {
return sleepSecond()
.then(_.partial(byConstant, n))
.then(print);
};
};
numbers.map(pauseMulAndPrint)
.reduce(Q.when, Q())
.done();
// ... 6 ... 2 ... 14
Promises handle single event very well
Extra complexity trying to handle sequence of events
var sleepSecond = _.partial(Q.delay, 1000);
var pauseMulAndPrint = function (n) {
return function () {
return sleepSecond()
.then(_.partial(byConstant, n))
.then(print);
};
};
numbers.map(pauseMulAndPrint)
.reduce(Q.when, Q())
.done();
// ... 6 ... 2 ... 14
Constructing a promise for each number :(
var sleepSecond = _.partial(Q.delay, 1000);
var pauseMulAndPrint = function (n) {
return function () {
return sleepSecond()
.then(_.partial(byConstantAsync, n))
.then(print);
};
};
"shoot the same bullet more than once"
Widely used in browser code
$(el).on('click', action) window.on('resize', cb) socket.on('message', handle)
We can extend the same approach to any event generator (like number sequence)
// action
var events = require('events');
var numberEmitter = new events.EventEmitter();
numberEmitter.on('number',
_.compose(print, byConstant)
);
Single code pipeline
var k = 0;
var ref = setInterval(function () {
numberEmitter.emit('number', numbers[k++]);
if (k >= numbers.length) {
clearInterval(ref);
}
}, 1000);
// prints 6, 2 14 with 1 second intervals
Goal: process number with 1 second pauses
numberEmitter
.on('number', action);
[3, 1, 7]
.map(mulBy2)
.forEach(print);
source of number events
.map(an async callback)
.buffer(n)
.forEach(another async callback);
source of number events
source
.map
fn
.buffer
3
.forEach
fn
.forEach(an async callback)
.map(an async callback)
.buffer(3)
source of number events
source
.map
fn
.buffer
3
.forEach
fn
.forEach(an async callback)
.map(an async callback)
.buffer(3)
3
6
6,
source of number events
.map
fn
.buffer
3
.forEach
fn
.forEach(an async callback)
.map(an async callback)
.buffer(3)
1
2
2,
source
6,
source of number events
.map
fn
.buffer
3
.forEach
fn
.forEach(an async callback)
.map(an async callback)
.buffer(3)
7
14
14
source
2,
6,
source of number events
.map
fn
.buffer
3
.forEach
fn
.forEach(an async callback)
.map(an async callback)
.buffer(3)
6, 2, 14
source
var stepEmitter = {
};
stepEmitter(numbers)
.map(an async callback)
map: function (cb) {
var emitter = new events.EventEmitter();
return _.extend(emitter, stepEmitter);
}
var stepEmitter = {
};
stepEmitter(numbers)
.map(an async callback)
map: function (cb) {
var emitter = new events.EventEmitter();
this.on('step', function (value) {
Q.when(cb(value))
.then(output => emitter.emit('step', output))
});
return _.extend(emitter, stepEmitter);
}
var stepEmitter = {
// same map and forEach methods as above
// returns a new step emitter that accumulates N items
// emits the entire array with N items as single argument
buffer: function (n) {
var received = [];
var emitter = new events.EventEmitter();
this.on('step', function (value) {
received.push(value);
if (received.length === n) {
emitter.emit('step', received);
received = [];
}
});
return _.extend(emitter, stepEmitter);
}
};
source(numbers)
.map(byConstant)
.buffer(3)
.forEach(print);
// sleeps 3 seconds then prints [6, 2, 14]
What about buffering events?
source(numbers)
.map(byConstant) // data transform
.buffer(3) // sequence control
.forEach(print); // data transform
We implemented 2 types of operations on a sequence of events
.map, .filter
// vs
.buffer
Use a reactive FP library
var Rx = require('rx');
var timeEvents = Rx.Observable
.interval(1000)
.timeInterval(); // stream 1
var numberEvents = Rx.Observable
.fromArray(numbers); // stream 2
Rx.Observable.zip(timeEvents, numberEvents,
function pickValue(t, n) { return n; })
.map(byConstant)
.subscribe(print)
// prints 6 2 14 with 1 second intervals
User click events
var Rx = require('rx');
var clickEvents = Rx.Observable
.fromEvent(document.querySelector('#btn'), 'click')
var numberEvents = Rx.Observable
.fromArray(numbers);
function pickSecond(c, n) { return n; }
Rx.Observable.zip(clickEvents, numberEvents,
pickSecond)
.map(byConstant)
.subscribe(print);
// prints a number on each button click
Marble diagram for
zip(s1, s2, fn)
function searchWikipedia (term) {
return $.ajax({
url: 'http://en.wikipedia.org/w/api.php',
dataType: 'jsonp',
data: {
action: 'opensearch',
format: 'json',
search: term
}
}).promise();
}
Autocomplete
Hard problem: need to throttle, handle out of order returned results, etc.
var keyups = Rx.Observable.fromEvent($('#input'), 'keyup')
.map(function (e) { return e.target.value; })
.filter(function (text) { return text.length > 2; });
Autocomplete with RxJs
keyups.throttle(500)
.distinctUntilChanged()
.flatMapLatest(searchWikipedia)
.subscribe(function (data) {
// display results
});
function add(a, b) {
return a + b
}
Remember pure functions?
const F = compose(f, g, h)
F(x)
function main() {
window.title = 'hi'
}
function main() {
const title = 'hi'
return function foo() {
window.title = title
}
}
// pure
// dirty
const app = main()
app()
// pure
// dirty
function main(win) {
const title = 'hi'
return function foo() {
win.title = title
}
}
// pure
// dirty
const app = main(window)
app()
// pure
// dirty
function main({DOM}) {
const keyups$ = DOM.select(...)
return {
DOM: keyups$.map(...vdom...)
}
}
all inputs are streams
all outputs are streams
function main({DOM}) {
const keyups$ = DOM.select(...)
return {
DOM: keyups$.map(...vdom...)
}
}
// pure
Cycle.run(main, {
DOM: makeDomDriver...
})
// dirty
All examples shown use today's ES5 standard
ES6 (EcmaScript2015) will make some pieces simpler
#OSCON @bahmutov
By Gleb Bahmutov
How do we convince / train developers used to imperative programming style to switch to functional or even reactive programming? I show the same example implemented in multiple styles to highlight the advantages one would get by going from imperative to object-oriented, functional, lazy evaluation, promise-based, event emitters, and finally reactive approach. The short examples are in JavaScript - the most widely used functional language today.
JavaScript ninja, image processing expert, software quality fanatic