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

https://github.com/ilyavf/monadic-composition