Monad - design pattern to deal with side effects keeping your functions pure.
Design pattern that allows you to write programs in a more abstract way by taking away some boilerplate code that's needed by the program logic.
The idea is that gluing (side effects, io) is made outside of the main functions (which can stay pure), but inside the compose helper.
↧
const add5 = x => x + 5
const double = x => x * 2
const deduct3 = x => x - 3
Ex. 1: Build application as a composition of "base" functions
const add5 = x => x + 5
const double = x => x * 2
const compose = (f, g) => x => g(f(x)))
const app = compose(add5, double)
app(3)
// >>> (3 + 5) * 2 = 16
const compose = fns => (x => {
return fns.reduce((acc, fn) => {
return fn(acc)
}, x)
})
const app = compose([
add5,
double,
deduct3
])
app(5)
// >>> 17
const app2 = compose([
app,
double
])
app2(5)
// >>> 34
const add5 = x => x + 5
const double = x => x * 2
const deduct3 = x => x - 3
Ex. 2. New requirement: explain how the result was calculated: 10 + 5 - 3 * 2 = 24
const compose = fns => x => {
return fns.reduce((acc, fn) => {
return fn(acc)
}, x)
}
const app = compose([
add5,
deduct3,
double,
])
const compose = fns => x => {
return fns.reduce(([x, desc], fn) => {
const [x2, desc2] = fn(x)
return [x2, desc + ' ' + desc2]
}, [x, ''])
}
function run(x) {
const [y, desc] = app(x)
console.log(x + desc + ' = ' + y)
}
run(10)
// >>> 10 + 5 - 3 * 2 = 24
app(10)
// >>> [24, "+ 5 - 3 * 2"]
[ , '+ 5']
[ , '* 2']
[ , '- 3']
const add5 = x => x + 5
const double = x => x * 2
Ex. 3: Add asynchronous function
const compose = fns => x => {
return fns.reduce((acc, fn) => {
return fn(acc)
}, x)
}
const asyncOp = x => Promise.resolve(x + 100)
// asyncOp(5).then(res => console.log(res))
const app = compose([
add5,
double,
deduct3,
asyncOp
])
const compose = fns => x => {
return fns.reduce((accP, fn) => {
return accP.then(acc => fn(acc))
}, Promise.resolve(x))
}
function run(x) {
const resultPromise = app(x)
resultPromise.then(result => console.log(result))
}
run(5)
// >>> 117
const add5 = x => [x + 5, '+ 5']
const double = x => [x * 2, '* 2']
const deduct3 = x =>
Promise.resolve([x - 3, '- 3'])
const compose = fns => x => {
return fns.reduce((acc, fn) => {
return acc.then(([x, desc]) => {
let res = fn(x)
if (!(res instanceof Promise)) {
res = Promise.resolve(res)
}
return res.then(([x2, desc2]) => {
return [x2, desc + ' ' + desc2]
})
})
}, Promise.resolve([x, '']))
}
Ex. 4: All together
const app = compose([
add5,
double,
deduct3
])
const app2 = compose([
app,
double
])
function run(x) {
app2(x).then(([y, desc]) => {
console.log(x + desc + '=' + y)
})
}
run(5)
// >>> 5 + 5 * 2 - 3 * 2 = 34
- Allow construct application using composition.
const app = compose([ add5, double, deduct3 ])
const compose = fns => x => { return fns.reduce((acc, fn) => { return acc.then(([x, desc]) => { let res = fn(x) if (!(res instanceof Promise)) { res = Promise.resolve(res) } return res.then(([x2, desc2]) => { return [x2, desc + ' ' + desc2] }) }) }, Promise.resolve([x, ''])) }
- Hide boilerplate by making gluing inside helpers.
- Better structure code: composition VS compose helpers.
Monads - function composition on steroids
http://pkaczor.blogspot.com/2013/09/monads-function-composition-on-steroids.html
Real example - blockchain transaction builder
https://www.npmjs.com/package/tx-builder
This talk with code source