From 9732d00f92bdf0544e2c658071e2674ab5874847 Mon Sep 17 00:00:00 2001 From: Vesa Karvonen Date: Sat, 17 Nov 2018 12:55:25 +0200 Subject: [PATCH] More support for asynchronous operations --- src/partial.lenses.js | 81 ++++++++++++++++++++++++++++++++++++++++--- test/tests.js | 12 +++++++ test/types.js | 10 ++++++ 3 files changed, 99 insertions(+), 4 deletions(-) diff --git a/src/partial.lenses.js b/src/partial.lenses.js index ae353afe..8ea668e2 100644 --- a/src/partial.lenses.js +++ b/src/partial.lenses.js @@ -167,6 +167,25 @@ const copyToFrom = (process.env.NODE_ENV === 'production' // +const selectAsync = (step, at, s, x, y) => { + const cont = v => (void 0 !== v ? v : next()) + const next = () => { + if (void 0 !== (s = step(s, x, y))) { + const vP = at(s, x, y) + return I.isThenable(vP) ? I.thenU(cont, vP) : cont(vP) + } + } + return next() +} + +const arrayStep = (i, xs) => (++i < xs[I.LENGTH] ? i : void 0) +const arrayAt = (i, xs, xi2v) => xi2v(xs[i], i) + +const selectInArrayLikeAsync = (xi2v, xs) => + selectAsync(arrayStep, arrayAt, -1, xs, xi2v) + +// + function selectInArrayLike(xi2v, xs) { for (let i = 0, n = xs[I.LENGTH]; i < n; ++i) { const v = xi2v(xs[i], i) @@ -634,7 +653,7 @@ const branchOr1LevelIdentity = (process.env.NODE_ENV === 'production' return written ? (same && xO === x ? x : r) : x }) -const branchOr1Level = (otherwise, k2o) => (x, _i, A, xi2yA) => { +const branchOr1Level = (otherwise, k2o, ks) => (x, _i, A, xi2yA) => { const xO = x instanceof Object ? toObject(x) : I.object0 if (I.Identity === A) { @@ -650,6 +669,34 @@ const branchOr1Level = (otherwise, k2o) => (x, _i, A, xi2yA) => { if (void 0 !== y) return y } } + } else if (SelectAsync === A) { + return I.IdentityAsync.chain( + v => + void 0 !== v + ? v + : selectAsync( + arrayStep, + (i, xks, k2o) => { + const k = xks[i] + if (void 0 === k2o[k]) { + return otherwise(xO[k], k, A, xi2yA) + } + }, + -1, + I.keys(xO), + k2o + ), + selectAsync( + arrayStep, + (i, ks) => { + const k = ks[i] + return k2o[k](xO[k], k, A, xi2yA) + }, + -1, + ks, + k2o + ) + ) } else { const {map, ap, of} = A let xsA = of(cpair) @@ -671,11 +718,13 @@ const branchOr1Level = (otherwise, k2o) => (x, _i, A, xi2yA) => { function branchOrU(otherwise, template) { const k2o = I.create(null) + const ks = [] for (const k in template) { + ks.push(k) const v = template[k] k2o[k] = I.isObject(v) ? branchOrU(otherwise, v) : toFunction(v) } - return branchOr1Level(otherwise, k2o) + return branchOr1Level(otherwise, k2o, ks) } const replaced = (inn, out, x) => (I.acyclicEqualsU(x, inn) ? out : x) @@ -938,6 +987,8 @@ const elemsI = (xs, _i, A, xi2yA) => ? mapPartialIndexU(xi2yA, xs, void 0) : A === Select ? selectInArrayLike(xi2yA, xs) + : A === SelectAsync + ? selectInArrayLikeAsync(xi2yA, xs) : traversePartialIndex(A, xi2yA, xs, void 0) // @@ -1369,7 +1420,13 @@ export const seemsArrayLike = x => export {Identity, IdentityAsync} from './ext/infestines' -export const Select = ConstantWith((l, r) => (void 0 !== l ? l : r)) +const apSelect = (l, r) => (void 0 !== l ? l : r) + +export const Select = ConstantWith(apSelect) + +export const SelectAsync = ConstantWith(function apSelectAsync(f, x) { + return I.isThenable(f) ? I.thenU(f => apSelect(f, x), f) : apSelect(f, x) +}) export const toFunction = (process.env.NODE_ENV === 'production' ? id @@ -1564,6 +1621,11 @@ export const reIx = o => { export const skipIx = setName(tieIx(I.sndU), 'skipIx') +// Async + +export const awaitIt = (x, i, F, xi2yF) => + I.isThenable(x) ? I.thenU(x => xi2yF(x, i), x) : xi2yF(x, i) + // Debugging export function getLog(l, s) { @@ -1646,6 +1708,8 @@ export const elemsTotal = (xs, i, A, xi2yA) => ? mapPartialIndexU(xi2yA, xs, mapPartialIndexU) : A === Select ? selectInArrayLike(xi2yA, xs) + : A === SelectAsync + ? selectInArrayLikeAsync(xi2yA, xs) : traversePartialIndex(A, xi2yA, xs, traversePartialIndex) : A.of(xs) @@ -1700,7 +1764,10 @@ export function matches(re) { } } -export const values = setName(branchOr1Level(identity, I.protoless0), 'values') +export const values = setName( + branchOr1Level(identity, I.protoless0, I.array0), + 'values' +) export function children(x, i, C, xi2yC) { return I.isArray(x) @@ -1890,6 +1957,12 @@ export function get(l, s) { export const getAs = I.curry(getAsU) +export const getAsAsync = I.curry(function getAsAsync(f, t, s) { + return I.resolve(toFunction(t)(s, void 0, SelectAsync, f)) +}) + +export const getAsync = getAsAsync(id) + export const isDefined = I.curry(function isDefined(t, s) { return void 0 !== getAsU(id, t, s) }) diff --git a/test/tests.js b/test/tests.js index 29b1d56d..5814cf89 100644 --- a/test/tests.js +++ b/test/tests.js @@ -242,6 +242,7 @@ describe('arities', () => { attemptEveryDown: 1, attemptEveryUp: 1, attemptSomeDown: 1, + awaitIt: 4, branch: 1, branchOr: 2, branches: 0, @@ -294,6 +295,8 @@ describe('arities', () => { fromFantasyMonad: 1, get: 2, getAs: 3, + getAsAsync: 3, + getAsync: 2, getInverse: 2, getLog: 2, getter: 1, @@ -2996,6 +2999,15 @@ describe('ix', () => { }) describe('async', () => { + testEq( + () => + L.getAsync( + [L.elems, later(2), L.awaitIt, L.when(x => 3 < x)], + [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5] + ), + 4 + ) + testEq(() => L.modifyAsync(L.elems, x => later(5, -x), [3, 1, 4]), [ -3, -1, diff --git a/test/types.js b/test/types.js index e836af56..e1b7c279 100644 --- a/test/types.js +++ b/test/types.js @@ -75,6 +75,8 @@ export const IdentityAsync = T_monad export const Select = T_applicative +export const SelectAsync = T_applicative + export const toFunction = T.fn( [T_optic], T_opticFnOf(T.or(T_monad, T_applicative, T_functor)) @@ -143,6 +145,10 @@ export const setIx = T.fn([T_index], T_optic) export const skipIx = T.fn([T_optic], T_optic) export const tieIx = T.fn([T.fn([T_index, T_index], T_index), T_optic], T_optic) +// Async + +export const awaitIt = T_optic + // Debugging export const getLog = T.fn([T_traversal, T_maybeDataI], T_maybeDataO) @@ -271,11 +277,15 @@ export const forEachWith = T.fn( export const get = T.fn([T_traversal, T_maybeDataI], T_maybeDataO) +export const getAsync = get + export const getAs = T.fn( [T.fn([T_maybeDataO, T_index], T.any), T_traversal, T_maybeDataI], T.any ) +export const getAsAsync = getAs + export const isDefined = T.fn([T_traversal, T_maybeDataI], T.boolean) export const isEmpty = T.fn([T_traversal, T_maybeDataI], T.boolean)