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()