* Functional
source: Improved Minesweeper
Node, C++, C#,
Angular, Vue.js, React, server-side etc.
event analysis and statistics for financial markets
Boston and New York
number of engineers
lifetime of the company
const todos = getTodos()
const updatedTodos = addNewTodo(todos, newTodo)
saveTodos(updatedTodos)where does it get the data?
how does it save the data?
is this object modified?
const todos = getTodos()
const updatedTodos = addNewTodo(todos, newTodo)
saveTodos(updatedTodos)what if getTodos() is called again
while the first save is still executing?
const todos = getTodos()
const updatedTodos = addNewTodo(todos, newTodo)
saveTodos(updatedTodos)functional: A way to write your program in terms of simple functions
Everyone has their own path to JS
var numbers = [3, 2, 8]
var constant = 2
// 4 16Problem: given an array of numbers, multiply even values by a constant and print the result
Write a solution using a for loop
Start using https://tonicdev.com/bahmutov/fun
var numbers = [3, 2, 8]
var constant = 2
var k = 0
for (k = 0; k < numbers.length; k += 1) {
if (numbers[k] % 2 === 0) {
console.log(numbers[k] * constant)
}
}We command the computer to iterate through the list of numbers
var numbers = [3, 2, 8]
var constant = 2
var k = 0
for (k = 0; k < numbers.length; k += 1) {
if (numbers[k] % 2 === 0) {
console.log(numbers[k] * constant)
}
}var numbers = [3, 2, 8]
var constant = 2
var k = 0
for (k = 0; k < numbers.length; k += 1) {
if (numbers[k] % 2 === 0) {
console.log(numbers[k] * constant)
}
}for loop at some point will betray you
var numbers = [3, 2, 8]
var constant = 2
var k = 0
for (k = 0; k < numbers.length; k += 1) {
if (numbers[k] % 2 === 0) {
console.log(numbers[k] * constant)
}
}var k = 0
for (k = 0; k < numbers.length; k += 1) {
if (numbers[k] % 2 === 0) {
console.log(numbers[k] * constant)
}
}Problem: extract functions that can be described with a single sentence
function mul(a, b) {
return a * b
}
function print(x) {
console.log(x)
}
function isEven(x) {
return x % 2 === 0
}
for (k = 0; k < numbers.length; k += 1) {
if (isEven(numbers[k])) {
print(mul(numbers[k], constant))
}
}multiplies two numbers
prints one argument
returns true if x is even
var k = 0
for (k = 0; k < numbers.length; k += 1) {
if (numbers[k] % 2 === 0) {
console.log(numbers[k] * constant)
}
}function mul(a, b) {
return a * b
}
function print(x) {
console.log(x)
}
function isEven(x) {
return x % 2 === 0
}
for (k = 0; k < numbers.length; k += 1) {
if (isEven(numbers[k])) {
print(mul(numbers[k], constant))
}
}Hmm, did we just blow up the size of the code for no reason?
initial spaghetti code
mul
isEven
...
short readable code
"atoms"
compare: multiply even numbers by a constant
if (numbers[k] % 2 === 0) {
console.log(numbers[k] * constant)
}if (isEven(numbers[k])) {
print(mul(numbers[k], constant))
}almost reads like the problem itself
function mul(a, b) {
return a * b
}
function print(x) {
console.log(x)
}
function isEven(x) {
return x % 2 === 0
}Problem: which function is NOT like the other two?
hint: look where the variables are coming from
function mul(a, b) {
return a * b
}
function print(x) {
console.log(x)
}
function isEven(x) {
return x % 2 === 0
}Problem: which function is NOT like the other two?
function mul(a, b) {
return a * b
}
only uses its arguments
same arguments => same result
does not modify the outside environment
function print(x) {
console.log(x)
}
uses outside variable
modifies the outside environment
function mulBy(K) {
return function (x) {
return K * x
}
}function mulBy(K) {
return function (x) {
return K * x
}
}function mulBy(K) {
return function (x) {
return K * x
}
}pure
impure
function mulBy(K) {
return function (x) {
return K * x
}
}
const double = mulBy(2)
console.log(double.toString())
// function (x) {
// return K * x
// }function mulBy(K) {
return function (x) {
return K * x
}
}
const double = mulBy(2)
/*
double is same as
K = 2
function (x) {
return K * x
}
*/function mul(a, b) {
return a * b
}
const constant = 2
for (...) {
mul(constant, numbers[k]))
}function and one argument are known early
function mul(a, b) {
return a * b
}
function mulBy = /* something here */;
var constant = 2
var byConstant = mulBy(constant)
for (...) {
byConstant(numbers[k])
}function mul(a, b) {
return a * b
}
function mulBy(K) {
return function(x) {
return mul(K, x)
}
}
const constant = 2
const byConstant = mulBy(constant)
for (...) {
byConstant(numbers[k])
}function mul(a, b) {
return a * b
}
const mulBy = K => x => mul(K, x)
const constant = 2
const byConstant = mulBy(constant)
for (...) {
byConstant(numbers[k])
}function mul(a, b) {
return a * b
}
const mulBy = K => x => mul(K, x)
const constant = 2
const byConstant = mulBy(constant)
for (...) {
byConstant(numbers[k])
}function mul(a, b) {
return a * b
}
const mulBy = K => x => K * x
const constant = 2
const byConstant = mulBy(constant)
for (...) {
byConstant(numbers[k])
}const mulBy = K => x => K * x
const double = mulBy(2)
console.log(double.toString())
// x => K * xhint: "eval" uses current lexical scope
// >>> insert statement here <<<
const triple
= eval('(' + double.toString() + ')')
console.log(triple(10))
// 30K = 3
// eval('(' + double.toString() + ')')
const triple = eval('(x => K * x)')
console.log(triple(10))
// 30function mul(a, b) {
return a * b
}
const constant = 2
const byConstant = /* something here */hint: look at Function.prototype.bind
function mul(a, b) {
return a * b
}
const constant = 2
const byConstant = mul.bind(null, constant)
// byConstant(10) = 20
// byConstant(-1) = -2function mul(a, b) {
return a * b
}
const constant = 2
const byConstant = mul.bind(null, constant)function savePassword(userId, newPassword)
// vs
function savePassword(newPassword, userId)function savePassword(userId, newPassword) {...}
function onLogin(userId) {
return {
savePassword: savePassword.bind(null, userId),
deleteAccount: ...
}
}
function mul(a, b) {
return a * b
}
function print(x) {
console.log(x)
}
function isEven(x) {
return x % 2 === 0
}
const byConstant = mul.bind(null, constant)
var k
for (k = 0; k < numbers.length; k += 1) {
if (isEven(numbers[k])) {
print(byConstant(numbers[k]))
}
}fn
fn
data
for (k = 0; k < numbers.length; k += 1) {
if (isEven(numbers[k])) {
print(byConstant(numbers[k]))
}
}fn
fn
data
mulAndPrint = compose(print, byConstant)
mulAndPrint(numbers[k])mulAndPrint = compose(print, byConstant)
mulAndPrint(numbers[k])mulAndPrint = compose(print, byConstant)
mulAndPrint(numbers[k])function compose()mulAndPrint = compose(print, byConstant)
mulAndPrint(numbers[k])function compose(f, g)mulAndPrint = compose(print, byConstant)
mulAndPrint(numbers[k])function compose(f, g) {
return function() {
}
}mulAndPrint = compose(print, byConstant)
mulAndPrint(numbers[k])function compose(f, g) {
return function(x) {
}
}mulAndPrint = compose(print, byConstant)
mulAndPrint(numbers[k])function compose(f, g) {
return function(x) {
return f(g(x))
}
}mulAndPrint = compose(print, byConstant)
for (k = 0; k < numbers.length; k += 1) {
if (isEven(numbers[k])) {
mulAndPrint(numbers[k])
}
}fn
data
function compose(f, g) {
return function(x) {
return f(g(x))
}
}const F = compose(f, g)
F(x)mulAndPrint = compose(print, byConstant)print "pollutes" the composed function
initial spaghetti code
mul
isEven
...
short readable code
.bind, compose
const byConstant = mul.bind(null, constant)
const mulAndPrint = compose(print, byConstant)
var k = 0
for (k = 0; k < numbers.length; k += 1) {
if (isEven(numbers[k])) {
mulAndPrint(numbers[k])
}
}var k
for (k = 0; k < list.length; k += 1) {
// call fn(list[k])
}var k = 0
for (k = 0; k < numbers.length; k += 1) {
if (isEven(numbers[k])) {
print(byConstant(numbers[k]))
}
}filter
map
side effect
function filter(fn, list) {
var k = 0
const result = []
for (k = 0; k < list.length; k += 1) {
if (fn(list[k])) {
result.push(list[k])
}
}
return result
}
// filter(isEven, [1, 2, 3])
// [2]callback first
filter(isEven, [1, 2, 3])evenNumbers = filter.bind(null, isEven)
evenNumbers([1, 2, 3])
// [2]evenNumbers = filter.bind(null, isEven)
evenNumbers([1, 2, 3])
// [2]function map(fn, list) {
var k = 0
const result = []
for (k = 0; k < list.length; k += 1) {
result.push(fn(list[k]))
}
return result
}
// map(byConstant, [1, 2, 3])
// [2, 4, 6]map(byConstant, numbers)multiplyAll = map.bind(null, byConstant)
multiplyAll([1, 2, 3])
// [2, 4, 6]function forEach(fn, list) {
var k = 0
for (k = 0; k < list.length; k += 1) {
fn(list[k])
}
}
// forEach(print, [1, 2, 3])
// 1 2 3forEach(print, map(byConstant, filter(isEven, numbers)))const onlyEven = filter.bind(null, isEven)
forEach(print, map(byConstant, onlyEven(numbers)))const onlyEven = filter.bind(null, isEven)
const multiply = map.bind(null, byConstant)
forEach(print, multiply(onlyEven(numbers)))const onlyEven = filter.bind(null, isEven)
const multiply = map.bind(null, byConstant)
const printAll = forEach.bind(null, print)
printAll(multiply(onlyEven(numbers)))const onlyEven = filter.bind(null, isEven)
const multiply = map.bind(null, byConstant)
const printAll = forEach.bind(null, print)
printAll(multiply(onlyEven(numbers)))const onlyEven = filter.bind(null, isEven)
const multiply = map.bind(null, byConstant)
const printAll = forEach.bind(null, print)
const compose = (f, g, h) => x => f(g(h(x)))
const solution = compose(printAll, multiply, onlyEven)
solution(numbers)fn
data
const onlyEven = filter.bind(null, isEven)
const multiply = map.bind(null, byConstant)
const printAll = forEach.bind(null, print)
const compose = (f, g, h) => x => f(g(h(x)))
const solution = compose(printAll, multiply, onlyEven)
solution(numbers)ugly!
// filter is a binary function
const onlyEven = filter(isEven)
onlyEven([1, 2, 3])
// [2]function map(fn) {
return function(list) {
var k = 0
const result = []
for (k = 0; k < list.length; k += 1) {
result.push(fn(list[k]))
}
return result
}
}
const multiply = map(byConstant)
multiply([1, 2, 3])const onlyEven = filter(isEven)
const multiply = map(byConstant)
const printAll = forEach(print)
const compose = (f, g, h) => x => f(g(h(x)))
const solution = compose(printAll, multiply, onlyEven)
solution(numbers)const onlyEven = filter(isEven)
const multiply = map(byConstant)
const printAll = forEach(print)
const compose = (f, g, h) => x => f(g(h(x)))
const solution = compose(printAll, multiply, onlyEven)
solution(numbers)multiply even numbers by a constant and print the result
const onlyEven = filter(isEven)
const multiply = map(byConstant)
const printAll = forEach(print)
const pipe = (f, g, h) => x => h(g(f(x)))
const solution = pipe(onlyEven, multiply, printAll)
solution(numbers)function map(fn, list) { ... }
curry(map)(byConstant)([1, 2, 3])
// [2, 4, 6]
function curry(fn) {
return function (x) {
return function (y) {
return fn(x, y)
}
}
}const curry = fn => x => y => fn(x, y)const map = curry((fn, list) => {
var k = 0
const result = []
for (k = 0; k < list.length; k += 1){
result.push(fn(list[k]))
}
return result
})const solution = pipe(
filter(isEven),
map(byConstant),
forEach(print)
)
solution(numbers)const solution = pipe(
filter(x => isEven(x)),
map(y => byConstant(y)),
forEach(z => print(z))
)
solution(numbers)Anonymous arrow functions are redundant
const solution = pipe(
filter(isEven),
map(byConstant),
forEach(print)
)
solution(numbers)
const solution = pipe(
filter(isEven),
map(byConstant),
forEach(print)
)
solution(numbers)// filter :: fn => [x] => [x]
// [x] => [x]
// [x] => [x]
// [x] => undefined
// solution :: [x] => undefined
// numbers is [x]
// undefined
console.log(['1','2','3'].map(parseFloat))
// 1, 2, 3console.log(['1','2','3'].map(parseInt))
// 1, NaN, NaN// parseFloat :: string => number
console.log(['1','2','3'].map(parseFloat))// parseInt :: string, radix => number
console.log(['1','2','3'].map(parseInt))
// 1, NaN, NaN// Array.prototype.map :: cb
// where cb :: item, index, array// parseInt :: string, radix => number
console.log(['1','2','3'].map(parseInt))
parseInt('1', 0) // 1
parseInt('2', 1) // NaN
parseInt('3', 2) // NaN// function parseInt(x, radix)
// but Array.map is (value, index, array)
['1', '2', '3'].map(function (x) {
return parseInt(x)
})// parseInt :: string, radix => number
unary(parseInt)('2', 1) // 2['1','2','3'].map(unary(parseInt))
// [1, 2, 3]// parseInt :: string, radix => number
const unary = f => x => f(x)
// unary(parseInt) :: string => number
['1','2','3'].map(unary(parseInt))
// [1, 2, 3]// parseInt :: string, radix => number
const parse10 = partialRight(parseInt, 10)['1','2','3'].map(parse10)
// [1, 2, 3]const partialRight = (f, b) => a => f(a, b)// parseInt :: string, radix => number
const parse10 = partialRight(parseInt, 10)
// parse10 :: string => number['1', '2', '3'].map(parse10)
// 1, 2, 3function fn(a, b, c) { ... }
var newFn = fn.bind(null, valueA, valueB);// or
var _ = require('lodash');
var newFn = _.partial(fn, valueA, valueB);
// or
var R = require('ramda');
var newFn = R.partial(fn, valueA, valueB);const base10 = _.partialRight(parseInt, 10)
['1', '2', '3'].map(base10);
// [1, 2, 3]
// radix is bound,
// index and array arguments are ignoredvar S = require('spots');
const base10 = S(parseInt, S, 10)
['1', '2', '3'].map(base10);
// [1, 2, 3]// divide by 10
function divide(a, b) { return a / b; }
var selective = require('heroin');
var by10 = selective(divide, { b: 10 });
console.log(by10(10)); // 1 (a = 10, b = 10)
console.log(by10(2)); // 0.2 (a = 2, b = 10)function fn(options) { ... }
var obind = require('obind');
var withBar = obind(fn, { bar: 'bar' });
withBar({ baz: 'baz' });
/*
equivalent to
foo({
bar: 'bar',
baz: 'baz'
})
*/var numbers = [3, 2, 8]
var constant = 2
// 4
// 16const R = require('ramda')
var numbers = [3, 2, 8]
var constant = 2
const solution = R.pipe(
R.filter(isEven),
R.map(R.multiply(constant)),
R.forEach(print)
)
solution(numbers)const R = require('ramda')
var numbers = [3, 2, 8]
var constant = 2
const solution = R.pipe(
R.filter(isEven),
R.map(R.multiply(constant)),
R.forEach(print)
)
solution(numbers)const R = require('ramda')
var numbers = [3, 2, 8]
var constant = 2
const solution = R.pipe(
R.filter(isEven),
R.map(R.multiply(constant)),
R.forEach(print)
)
solution(numbers)6 functions
2 values
forEach(console.log, [1, 2, 3])Error: Illegal invocation
forEach(console.log.bind(console), [1, 2, 3])const numbers = () => Promise.resolve([3, 2, 8])hint: look at R.pipeP
const numbers = () => Promise.resolve([3, 2, 8])
const solution = R.pipeP(
numbers,
R.filter(isEven),
R.map(R.multiply(constant)),
R.forEach(print)
)
solution()
// 4
// 16const Numbers = (numbers) => (
<ul>
{R.map(x => (<li>{x}</li>), numbers)}
</ul>
)
const Container = children => (
<div className="container">
<h1>Numbers are</h1>
{children}
</div>
)
const App = appState => (
Container(Numbers(appState.numbers))
)list iteration
composition
const App = appState => (
Container(Numbers(appState.numbers))
)// same as
const App = R.compose(
Container, Numbers, R.prop('numbers')
)list of books, tutorials, libraries and people