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