ðŸŠī\zansh.in\js

Play with the controls to see how the code works. All code is present in the page, but some scrollback may be necessary to grok everything.

# identity functor / applicative / monad / maybe

3 min read

Mathlessly journey from a basic Identity Functor to a Maybe Monad with an Applicative in-between.

# identity functor / just

const Just = x => ({
valueOf: () => x,
map: f => Just(f(x))
})
const run = () =>
Just(['world', 'hello'])
.map(x => x.reverse())
.map(x => x.join(', '))
.map(x => x + '!')
.valueOf()

# unary function

function unary(msg) {
console.log(msg)
}
const run = () => unary('hello, world!')

# unary applicative

const Applicative = fn => ({
ap: m => m.map(fn)
})
const run = () => Applicative(unary)
.ap(Just('hello, world!'))

# binary curried function

const reverse = first => second => {
return [second, first]
}
const run = () =>
reverse('world')('hello')

# identity applicative

const Just = x => ({
valueOf: () => x,
map: f => Just(f(x)),
ap: m => m.map(x)
})
const reverse = first => second => {
return [second, first]
}
const run = () =>
Just(reverse)
.ap(Just('world'))
.ap(Just('hello'))
.map(x => x.join(', '))
.map(x => x + '!')
.valueOf()

# identity monad

const Just = x => ({
valueOf: () => x,
map: f => Just(f(x)),
ap: m => m.map(x),
chain: f => f(x)
})
const reverse = first => second => {
return [second, first]
}
const run = () =>
Just(reverse)
.ap(Just('world'))
.ap(Just('hello'))
.map(x => x.join(', '))
.chain(x => Just(x + '!'))
.valueOf()

# just + nothing

const Just = x => ({
valueOf: () => x,
map: f => Just(f(x)),
ap: m => m.map(x),
chain: f => f(x)
})
const Nothing = () => ({
valueOf: () => undefined,
map: () => Nothing(),
ap: () => Nothing(),
chain: () => Nothing()
})
const just = () =>
Just('five boxing wizards').map(value =>
console.log('Just: ' + value)
)
const nothing = () =>
Nothing('five boxing wizards').map(() =>
console.log('Nothing?')
)

# maybe / orElse()

const Just = x => ({
valueOf: () => x,
map: f => Just(f(x)),
ap: m => m.map(x),
chain: f => f(x),
orElse: () => Just(x)
})
const Nothing = () => ({
valueOf: () => undefined,
map: () => Nothing(),
ap: () => Nothing(),
chain: () => Nothing(),
orElse: f => f()
})
function maybeDate(possibleDate) {
const isDate =
possibleDate instanceof Date &&
!isNaN(possibleDate)
return isDate
? Just(possibleDate)
: Nothing()
}
const parseDate = dateString =>
maybeDate(new Date(dateString))
.map(x => x.toLocaleDateString())
.orElse(() => Just('Not a date'))
.valueOf()

# MakeMaybe

const MakeMaybe = predicate => value => {
return predicate(value)
? Just(value)
: Nothing()
}
const maybeDate = MakeMaybe(
x => x instanceof Date && !isNaN(x)
)
const parseDate = dateString =>
maybeDate(new Date(dateString))
.map(x => x.toLocaleDateString())
.orElse(() => Just('Not a date'))
.valueOf()

# separate logic + runtime type-checking

const timeAndDate_logic =
time => date => {
const merged = new Date(date)
merged.setHours(time.getHours())
merged.setMinutes(time.getMinutes())
merged.setSeconds(0, 0)
return merged
}
const timeAndDate = (time, date) =>
Just(timeAndDate_logic)
.ap(maybeDate(new Date(`99/01/01 ${time}`)))
.ap(maybeDate(new Date(date)))
.map(x => x.toLocaleString())
.orElse(() => Just(`Bad arguments`))
.valueOf()

Google it:

ðŸŒļ ADT Algebraic Data Type 🌚 applicative 🌷 Array join ðŸŒŧ Array reverse 🌞 binary function ðŸŒđ Category Theory 💐 chaining ðŸŒļ closures 🌚 console 🌷 currying ðŸŒŧ Date constructor 🌞 identity functor ðŸŒđ identity monad 💐 isNaN ðŸŒļ just monad 🌚 maybe from 🌷 maybe monad ðŸŒŧ method chaining 🌞 nothing monad ðŸŒđ ternary operator 💐 toLocaleDateString ðŸŒļ unary function