Skip to content

Approximating Ramda functions with Partial Lenses

Vesa Karvonen edited this page Nov 9, 2018 · 40 revisions

Below is a non-exhaustive list of functions in the style of Ramda implemented using only Partial Lenses.

IMPORTANT: The below implementations are not necessarily perfect matches with similarly named Ramda functions. The below implementations are also not necessarily the best or closest possible implementations of those functions in terms of Partial Lenses.

The intention is to give the reader one way to gain some insight into the expressiveness of the Partial Lenses library.

It should also be noted that this is not how Partial Lenses are designed to be used. The idea with Partial Lenses is to compose an optic that targets a number of elements in a data structure and then lets you perform various operations on those elements, while also maintaining invariants of said data structure. Most of the below examples encapsulate the used optics, which means that they can no longer be used as optics, which essentially defeats the advantages that optics can provide for manipulating more complex data structures in a compositional manner.

Link to playground with the below code.

const F = L.set(L.identity, false)
const T = L.set(L.identity, true)
const add = (l, r) => L.get(L.add(r), l)
const adjust = (fn, i, xs) => L.modify(i, fn, xs)
const all = (c, xs) => L.all(c, L.elems, xs)
const always = L.set(L.identity)
const any = (c, xs) => L.any(c, L.elems, xs)
const append = L.set(L.append)
const assoc = L.set
const assocPath = L.set
const concat = (xs, ys) => L.collect([L.elems, L.elems], [xs, ys])
const cond = cs => L.get(L.cond(...cs))
const contains = (x, xs) => L.or([L.elems, L.is(x)], xs)
const countBy = (f, xs) => L.countsAs(f, L.elems, xs)
const dissoc = L.remove
const dissocPath = L.remove
const divide = (l, r) => L.get(L.divide(r), l)
const drop = (n, xs) => L.remove(L.prefix(n), xs)
const dropLast = (n, xs) => L.remove(L.suffix(n), xs)
const endsWith = (p, s) => L.isDefined(L.dropSuffix(p), s)
const evolve = (t, s) => L.transform(L.branch(L.modify(L.leafs, fn => [L.optional, L.modifyOp(fn)], t)), s)
const filter = (xib, xs) => L.remove([L.elems, L.unless(xib)], xs)
const find = (xib, xs) => L.select([L.elems, L.when(xib)], xs)
const findIndex = (xib, xs) => L.selectAs((x, i) => xib(x, i) ? i : undefined, L.elems, xs)
const flatten = L.collect(L.flatten)
const forEach = (xi2u, xs) => L.forEach(xi2u, L.elems, xs)
const head = L.get(0)
const identity = L.get(L.identity)
const ifElse = (c, t, e) => L.get(L.ifElse(c, t, e))
const indexOf = (x, xs) => findIndex(L.get(L.is(x)), xs)
const init = L.remove(L.last)
const insert = (i, x, xs) => L.set(L.slice(i,i), [x], xs)
const insertAll = (i, ys, xs) => L.set(L.slice(i, i), ys, xs)
const intersperse = (s, xs) => L.collect([L.elems, (x, i) => i ? [s, x] : [x], L.elems], xs)
const invertObj = L.get(L.applyAt(L.entries, L.reverse))
const join = (s, ss) => L.join(s, L.elems, ss)
const keys = L.collect(L.keys)
const last = L.get(L.last)
const lens = L.lens
const lensIndex = i => i
const lensPath = p => p
const lensProp = p => p
const map = L.modify(L.children)
const mean = L.mean(L.elems)
const merge = (l, r) => L.assign(L.identity, r, l)
const multiply = (l, r) => L.get(L.multiply(r), l)
const negate = L.get(L.negate)
const none = (c, xs) => L.none(c, L.elems, xs)
const not = L.get(L.complement)
const nth = L.get
const of = L.getInverse(0)
const omit = (ks, o) => L.remove(L.props(...ks), o)
const over = L.modify
const path = L.get
const pathEq = (p, x, o) => L.get([p, L.is(x)], o)
const pathOr = (d, p, x) => L.get([p, L.valueOr(d)], x)
const pathSatisfies = L.getAs
const pick = (ps, o) => L.get(L.props(...ps), o)
const pipe = (...fs) => L.get(L.compose(...fs)) // Pipe unary functions.
const pluck = (p, xs) => L.modify(L.elems, L.get(p), xs)
const prepend = (x, xs) => L.set(L.slice(0, 0), [x], xs)
const product = L.product(L.elems)
const prop = path // `prop*` === `path*`.  Could also have `nth*`.
const propEq = pathEq
const propOr = pathOr
const propSatisfies = pathSatisfies
const props = (ps, o) => L.collect([L.props(...ps), L.values], o)
const reduce = (sxis, s, xs) => L.foldl(sxis, s, L.elems, xs)
const reduceRight = (xsis, s, xs) => L.foldr((s, x, i) => xsis(x, s, i), s, L.elems, xs)
const reject = (xib, xs) => L.remove([L.elems, L.when(xib)], xs)
const remove = (i, n, xs) => L.remove(L.slice(i, i+n), xs)
const replace = (re, w, s) => L.set(L.matches(re), w, s)
const reverse = L.get(L.reverse)
const set = L.set
const slice = (si, ei, xs) => L.get(L.slice(si, ei), xs)
const split = (sep, s) => L.get(L.split(sep), s)
const splitAt = (n, xs) => [take(n, xs), drop(n, xs)]
const startsWith = (p, s) => L.isDefined(L.dropPrefix(p), s)
const subtract = (l, r) => L.get(L.subtract(r), l)
const sum = L.sum(L.elems)
const tail = L.remove(0)
const take = (n, xs) => L.get(L.prefix(n), xs)
const takeLast = (n, xs) => L.get(L.suffix(n), xs)
const test = (re, s) => L.isDefined(L.matches(re), s)
const toPairs = L.get(L.keyed)
const trim = L.remove(L.matches(/^\s+|\s+$/g))
const unless = (c, e) => L.get(L.ifElse(c, [], e))
const update = L.set
const values = L.collect(L.values)
const view = L.get
const when = (c, t) => L.get(L.ifElse(c, t, []))
const where = (t, s) => L.and(L.branch(t), s)
const whereEq = (t, s) => L.and(L.branch(L.modify(L.values, L.is, t)), s)
const zipObj = (ks, vs) => L.disperse(L.branches(...ks), vs, 'anything')