Last active
June 9, 2020 11:13
-
-
Save outbreak/39ef717126eb1dd6a4a6dc155e2cd9a8 to your computer and use it in GitHub Desktop.
Functional Lenses
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
// Operators | |
const eq = (a) => (b) => a === b | |
// Types | |
const typeOf = (a) => typeof a | |
const isArray = (a) => Array.isArray(a) | |
const isUndefined = (a) => eq('undefined')(typeOf(a)) | |
// Lists | |
const reverse = (xs) => xs.reverse() | |
const reduce = (fn) => (a) => (xs) => xs.reduce(fn, a) | |
const compose = (...fns) => (x) => reduce((acc, fn) => fn(acc))(x)(reverse(fns)) | |
// Objects | |
const assign = Object.assign | |
const prop = (key) => (object) => object[key] | |
const assoc = (key) => (value) => (object) => assign(isArray(object) ? [] : {}, object, {[key]: value}) | |
// Numbers | |
const inc = (n) => n + 1 | |
// Lenses | |
const Lens = (getter) => (setter) => compose(assoc('setter')(setter), assoc('getter')(getter))({}) | |
const Prop = (name) => Lens(prop(name))(assoc(name)) | |
const View = (lens) => (object) => lens.getter(object) | |
const Set = (lens) => (value) => (object) => lens.setter(value)(object) | |
const Over = (lens) => (fn) => (object) => lens.setter(fn(lens.getter(object)))(object) | |
const Combine = (...lenses) => { | |
const getter = (index) => (object) => isUndefined(object) || eq(index)(lenses.length) | |
? object | |
: getter(inc(index))(lenses[index].getter(object)) | |
const setter = (index) => (value) => (object) => eq(index)(lenses.length) | |
? value | |
: lenses[index].setter( setter(inc(index))(value)(lenses[index].getter(object)) )(object) | |
return Lens(getter(0))(setter(0)) | |
} | |
// Example | |
const o = {a: {b: {c: 0}}} | |
const a = Prop('a') | |
const b = Prop('b') | |
const d = Prop('d') | |
const ab = Combine(a, b) // Performs left-to-right lenses combination | |
console.log(View(ab)(o)) // { c: 0 } | |
console.log(View(ab)({})) // undefined | |
console.log(Set(ab)(1)(o)) // { a: { b: 1 } } | |
console.log(Over(ab)((v) => 10)(o)) // { a: { b: 10 } } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is fantastic. For compose did you try not doing
reverse
but just going opposite direction usingreduceRight
?