-
Notifications
You must be signed in to change notification settings - Fork 36
Approximating Immutability Helper with Partial Lenses
Vesa Karvonen edited this page Nov 9, 2017
·
6 revisions
Link to playground
// This is an approximation of
//
// https://github.com/kolodny/immutability-helper
//
// using Partial Lenses. A known semantic difference is the
// treatment of `undefined`. There may be other semantic
// differences that can likely be fixed.
const ops = {
$push: xs => [L.suffix(0), L.setOp(xs)],
$unshift: xs => [L.prefix(0), L.setOp(xs)],
$splice: args => L.seq(args.map(([b, n, ...xs]) => [L.slice(b, b+n), L.setOp(xs)])),
$set: L.setOp,
$unset: ps => [L.props(...ps), L.removeOp],
$merge: L.assignOp,
$apply: L.modifyOp,
$toggle: fs => L.seq(...fs.map(f => [f, L.modifyOp(x => !x)]))
}
const toTransform = immutabilityHelper => L.seq(
...Object.entries(immutabilityHelper)
.map(([key, value]) => ops[key] ? ops[key](value)
: [/^0|[1-9][0-9]*$/.test(key) ? L.ifElse(Array.isArray, Number(key), key) : key,
toTransform(value)]))
const update = (ds, op) => L.transform(toTransform(op), ds)
update.extend = (key, fn) => ops[key] = arg => L.modifyOp(x => fn(arg, x))
// --- Examples ---
log( 1, update(['x'], {$push: ['y']}) )
log( 2, update({}, {x: {y: {z: {$set: 7}}}, a: {b: {$push: [9]}}}) )
log( 3, update([1, 2, 3], {$push: [4]}) )
log( 4, update([1, 2, {a: [12, 17, 15]}], {2: {a: {$splice: [[1, 1, 13, 14]]}}}) )
log( 5, update({a: 5, b: 3}, {b: {$apply: x => x * 2}}) )
log( 6, update({a: 5, b: 3}, {$merge: {b: 6, c: 7}}) )
update.extend('$auto', (value, object) => update(object || {}, value))
update.extend('$autoArray', (value, object) => update(object || [], value))
log( 7, update({}, {
foo: {$autoArray: {
0: {$auto: {
bar: {$autoArray: {$push: ['x', 'y', 'z']}}
}}
}}
}))
update.extend('$addtax', (tax, original) => original + (tax * original))
log( 8, update({ price: 123 }, {price: {$addtax: 0.8}}) )
log( 9, update({a: false}, {$toggle: ['a']}) )
Link to playground
// Approximating https://github.com/kolodny/immutability-helper with Partial Lenses
const update = (state, op) => L.transform(op, state)
// --- Operations ---
const $push = xs => [L.suffix(0), L.setOp(xs)]
const $unshift = xs => [L.prefix(0), L.setOp(xs)]
const $splice = (b, n, ...xs) => [L.slice(b, b+n), L.setOp(xs)]
const $set = L.setOp
const $unset = (...ps) => [L.props(...ps), L.removeOp]
const $merge = L.assignOp
const $apply = L.modifyOp
// --- Template object combinator ---
const T = L.branch
// --- Examples ---
const log = console.log
// update(['x'], {$push: ['y']})
log( 1, update(['x'], $push(['y'])) )
// update(my, {x: {y: {z: {$set: 7}}}, a: {b: {$push: [9]}}})
log( 2, update({}, T({x: {y: {z: $set(7)}}, a: {b: $push([9])}})) )
// update([1, 2, 3], {$push: [4]})
log( 3, update([1, 2, 3], $push([4])) )
// update([1, 2, {a: [12, 17, 15]}], {2: {a: {$splice: [[1, 1, 13, 14]]}}})
log( 4, update([1, 2, {a: [12, 17, 15]}], [2, T({a: $splice(1, 1, 13, 14)})]) )
// update({a: 5, b: 3}, {b: {$apply: x => x * 2}})
log( 5, update({a: 5, b: 3}, T({b: $apply(x => x * 2)})) )
// update({a: 5, b: 3}, {$merge: {b: 6, c: 7}})
log( 6, update({a: 5, b: 3}, $merge({b: 6, c: 7})) )
// update({}, {
// foo: {$autoArray: {
// 0: {$auto: {
// bar: {$autoArray: {$push: ['x', 'y', 'z']}}
// }}
// }}
// })
log( 7, update({}, ["foo", 0, "bar", $push(['x', 'y', 'z'])]) )
// update.extend('$addtax', (tax, original) => original + (tax * original))
const $addTax = tax => $apply(original => original + (tax * original))
// update({ price: 123 }, {price: {$addtax: 0.8}})
log( 8, update({ price: 123 }, T({price: $addTax(0.8)})) )