Monadic Composition
Agenda
1. Pure functions
2. Function composition
3. Monadic composition
4. Coding exercise...
Pure functions
f = x → x + 5
✅
f = x → sqrt(x)
✅
f = x → (Date.now() - x)
❌
Function composition
c = f · g
c (x) = g ( f ( x ) )
Monadic composition
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.
Coding exercise:
Accounting applications
built with function composition
↧
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
Monadic composition
- 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.
Links
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