Last active
August 8, 2019 17:41
-
-
Save masaeedu/be84c9ac2b54fdb45321c8fb264f97ca to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { cata } = require("@masaeedu/fp") | |
const { adt, match } = require("@masaeedu/adt") | |
// All of these are common, reusable utilities you could get from a library | |
const classes = (() => { | |
const foldable = ({ foldMap }) => { | |
const fold = fn.flip(foldMap)(fn.identity) | |
return { fold } | |
} | |
const bifoldable = ({ bifoldMap }) => { | |
const lfoldMap = m => f => bifoldMap(m)(f)(fn.identity) | |
const rfoldMap = m => f => bifoldMap(m)(fn.identity)(f) | |
return { lfoldMap, rfoldMap } | |
} | |
return { foldable, bifoldable } | |
})() | |
const fn = (() => { | |
const identity = x => x | |
const compose = f => g => x => f(g(x)) | |
const flip = f => x => y => f(y)(x) | |
return { identity, compose, flip } | |
})() | |
const maybe = adt({ nothing: [], just: ["a"] }) | |
// :: identity a = a | |
const identity = (() => { | |
const map = fn.identity | |
const lift2 = fn.identity | |
const pure = fn.identity | |
const foldMap = _ => fn.identity | |
const traverse = _ => fn.identity | |
return { map, lift2, pure, foldMap, traverse } | |
})() | |
// :: compose f g a = f (g a) | |
const compose = f => g => { | |
const map = fn.compose(f.map)(g.map) | |
const lift2 = fn.compose(f.lift2)(g.lift2) | |
const pure = fn.compose(f.pure)(g.pure) | |
const foldMap = m => fn.compose(f.foldMap(m))(g.foldMap(m)) | |
const { fold } = classes.foldable({ foldMap }) | |
const traverse = a => fn.compose(f.traverse(a))(g.traverse(a)) | |
const filter = fn.compose(f.map)(g.filter) | |
return { map, lift2, pure, foldMap, fold, traverse, filter } | |
} | |
// :: absurd a = ⊥ | |
const absurd = (() => { | |
const is = _ => false | |
const absurd = _ => { throw "The impossible has happened, you're trying to evaluate a void value! Check your types!" } | |
const map = _ => absurd | |
return { map } | |
})() | |
// :: union f g a = f a | g a | |
const union = q => r => { | |
const is = x => q.is(x) || r.is(x) | |
const map = f => x => q.is(x) ? q.map(f)(x) : r.map(f)(x) | |
const foldMap = m => f => x => q.is(x) ? q.foldMap(m)(f)(x) : r.foldMap(m)(f)(x) | |
const { fold } = classes.foldable({ foldMap }) | |
const filter = f => x => q.is(x) ? q.filter(f)(x) : r.filter(f)(x) | |
return { is, map, foldMap, fold, filter } | |
} | |
const undefinedOr = (() => { | |
const nope = undefined | |
const yup = x => x | |
const match = ({ nope, yup }) => x => x === undefined ? nope : yup(x) | |
const is = _ => true | |
const map = f => match({ | |
nope, | |
yup: f | |
}) | |
const foldMap = m => f => match({ | |
nope: m.empty, | |
yup: f | |
}) | |
const { fold } = classes.foldable({ foldMap }) | |
const filter = f => match({ | |
nope, | |
yup: x => maybe.match({ nothing: nope, just: yup })(f(x)) | |
}) | |
return { nope, yup, match, is, map, foldMap, fold, filter } | |
})() | |
const array = (() => { | |
const is = x => Array.isArray(x) | |
const map = f => xs => xs.map(f) | |
const foldMap = m => f => xs => xs.reduce((p, c) => m.append(p)(f(c)), m.empty) | |
const { fold } = classes.foldable({ foldMap }) | |
const filter = f => xs => xs.reduce((p, c) => maybe.match({ nothing: p, just: x => [...p, c] })(f(c)), []) | |
return { is, map, foldMap, fold, filter } | |
})() | |
const prop = p => { | |
const is = x => Object.prototype.hasOwnProperty.call(x, p) | |
const map = f => o => ({ ...o, [p]: f(o[p]) }) | |
const foldMap = _ => f => ({ [p]: x }) => f(x) | |
const { fold } = classes.foldable({ foldMap }) | |
return { is, map, foldMap, fold } | |
} | |
const int = (() => { | |
const sum = { append: x => y => x + y, empty: 0 } | |
return { sum } | |
})() | |
const tuple = (() => { | |
const map = f => ([a, b]) => [a, f(b)] | |
const bifoldMap = m => f => g => ([a, b]) => m.append(f(a))(f(b)) | |
const { lfoldMap, rfoldMap } = classes.bifoldable({ bifoldMap }) | |
const foldMap = rfoldMap | |
const { fold } = classes.foldable({ foldMap }) | |
return { map, lfoldMap, rfoldMap, foldMap, fold } | |
})() | |
// :: cofree f a = [a, f (cofree f a)] | |
const cofree = f => { | |
const tf = compose(tuple)(f) | |
const map = ab => cata(tf)(([a, b]) => [f(a), b]) | |
const foldMap = m => am => cata(tf)(([a, fm]) => { | |
const m1 = am(a) | |
const m2 = f.fold(m)(fm) | |
return m.append(m1)(m2) | |
}) | |
const { fold } = classes.foldable({ foldMap }) | |
return { map, foldMap, fold } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment