Monads for the JS Developer

Callbacks as values

function readAndSendFile(callback) {
  readFile('/etc/shadow', function (err, data) {
    if (err) 
      callback(err);
    else
      sendFile(data.toString(), function(err, data){
        if (err) 
          callback(err);
        else
          callback(null, data);
      })
  });
}
function readAndSendFile() {
  return readFile('/etc/shadow')
    .then(data => {
      return sendFile(data.toString());
    })
  });
}

// Promise[SendResult]
readAndSendFile().then(result => {
  console.log(result) // not a promise!
})

What do promises have to do with Monads?

Promises are just one type of Monad.

If promises are just one type of Monad, what other types are there?

Arrays

[1, 3, 5].map(num => [num, num + 1]
// [[1, 2], [3, 4], [5, 6]] 




[1, 3, 5].flatMap(num => [num, num + 1])
// [1, 2, 3, 4, 5, 6]

readFile('/etc/shadow')
  .then(data => {
    return sendFile(data.toString());
  })
});

Options

Options

Optional - Java

Option - Scala

Maybe - Haskell

option - Reason

Maybe - Elm

const user = {name: 'paul'}

const src = user.image.src // error!

const srcSafe =
  (user && user.image && user.image.src) || 
  "default"

const user = {name: 'paul'}

const src = 
  option(user)
    .chain(u => option(u.image))
    .chain(img => option(img.src))
    .getOrElse("default")

A monad is:

1. A wrapper around a value

2. A 'chain' method which composes wrapped values.

option('foo')
  .chain(foo => option(foo.bar))
readFile('/etc/shadow')
  .then(data => sendFile(data.toString()))
[1, 3, 5]
  .flatMap(num => [num, num + 1])

Promise

Option

Array

Reader Monad - Dependency Injection

 

Writer Monad - Logging

 

State Monad - State Changes

 

Writer Monad - Errors

 

IO/Thunk Monad - Side Effects

github.com/fantasy/fantasy-land

but, why?

const biz = foo.bar.baz.biz


const baz = 
  op('Foo')
    .chain(foo => 
      op(foo.bar).chain(bar => 
        op(bar.baz).chain(baz =>
          op(baz.biz).map(biz =>
            biz
          )
        )
      )
    )

async function getUser(username){
  const user = await fetchUser(username)
  const profileImage = await fetchImage(user.img)
  const friend = await fetchUser(user.friends[0])
  
  return [user, profileImage, friend]
}
const baz = 
  op('Foo')
    .chain(foo => 
      op(foo.bar).chain(bar => 
        op(bar.baz).chain(baz =>
          op(baz.biz).map(biz =>
            biz
          )
        )
      )
    )
const baz = 
  do {
    foo << op('Foo')
    bar << op(foo.bar)
    baz << op(bar.baz)
    biz << op(baz.biz)

    biz
  }

babel-plugin-monadic-do

import { Async } from "crocks"

const read = Async.fromPromise(readFile)
const send = Async.fromPromise(sendFile)

const readAndSendFile = 
  do {
    data << read('/etc/shadow')
    resp << send(data.toString())

    resp
  }

FUN TIME

Render Props

<DataProvider>
  {data => (<h1>{data.target}</h1>)}
</DataProvider>
DataProvider(data => (
  <h1>{data.target}</h1>
))

github.com/pfgray/chainable-components

withState(0)
  .chain(outer => {
    return withState(outer.value + 5)
      .map(inner => {
        return ({inner, outer})
      })
  }
).render(({inner, outer}) => (
  <div>
    <div>
      Outer: {outer.value}
      <button onClick={() => outer.update(outer.value + 1)}>
        +
      </button>
    </div>
    <div>
      Inner: {inner.value}
      <button onClick={() => inner.update(inner.value + 1)}>
        +
      </button>
    </div>
  </div>
));

github.com/pfgray/chainable-components

do {
  token    << withAuth;
  route    << withRoute;
  apps     << withLoadablePromise(() => getApps(token));
  deleting << withState({ deleting: false, app: null });

  <div>
    {route.location.params.user}
    {apps.map(app => ...)}
  </div>
}

Monads for the JS Developer

By Paul Gray

Monads for the JS Developer

  • 514