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