From 6e440e8f9886746dd4bd44c17144d12f7b2f61b8 Mon Sep 17 00:00:00 2001 From: Vesa Karvonen Date: Tue, 6 Nov 2018 21:02:22 +0200 Subject: [PATCH 1/9] Setting up things to allow for more expressive pattern matching --- src/partial.lenses.js | 107 ++++++++++++++++++++++++++---------------- test/tests.js | 3 +- test/types.js | 2 +- 3 files changed, 70 insertions(+), 42 deletions(-) diff --git a/src/partial.lenses.js b/src/partial.lenses.js index b7592af6..e1c891f9 100644 --- a/src/partial.lenses.js +++ b/src/partial.lenses.js @@ -1057,14 +1057,25 @@ function nVars(n) { return vars } -const isPrimitive = x => x == null || typeof x !== 'object' +// + +const isPrimitive = x => { + if (x == null) return true + const t = typeof x + return t !== 'object' && t !== 'function' +} function match1(kinds, i, e, x) { if (void 0 !== x) { - if (i in e) return I.acyclicEqualsU(e[i], x) - e[i] = x - const k = kinds[i] - return !k || k(x) + const previous = e[i] + if (undefined !== previous) { + if (I.acyclicEqualsU(e[i], x)) return e + } else { + const k = kinds[i] + if (!k || k(x)) { + return setIndex(i, x, e) + } + } } } @@ -1138,17 +1149,9 @@ const checkPatternPairInDev = return deepFreezeInDev(ps) } -const setDefined = (o, k, x) => { - if (void 0 !== x) o[k] = x -} - -const pushDefined = (xs, x) => { - if (void 0 !== x) xs.push(x) -} - function toMatch(kinds, p) { if (void 0 === p || all1(isPrimitive, leafs, p)) { - return (e, x) => I.acyclicEqualsU(p, x) + return (e, x) => I.acyclicEqualsU(p, x) && e } else if (isVariable(p)) { const i = p[PAYLOAD][0][PAYLOAD] return i < 0 ? id : (e, x) => match1(kinds, i, e, x) @@ -1172,14 +1175,13 @@ function toMatch(kinds, p) { let l = x[I.LENGTH] if (void 0 !== spread ? l < n - 1 : l !== n) return const j = init[I.LENGTH] - for (let i = 0; i < j; ++i) if (!init[i](e, x[i])) return + for (let i = 0; i < j; ++i) if (!(e = init[i](e, x[i]))) return const k = rest[I.LENGTH] l -= k - for (let i = 0; i < k; ++i) if (!rest[i](e, x[l + i])) return - return ( - !(0 <= spread) || - match1(kinds, spread, e, copyToFrom(Array(l - j), 0, x, j, l)) - ) + for (let i = 0; i < k; ++i) if (!(e = rest[i](e, x[l + i]))) return + return 0 <= spread + ? match1(kinds, spread, e, copyToFrom(Array(l - j), 0, x, j, l)) + : e } } else { let spread = p[PAYLOAD] @@ -1197,7 +1199,7 @@ function toMatch(kinds, p) { for (const k in x) { const m = p[k] if (m) { - if (!m(e, x[k])) return + if (!(e = m(e, x[k]))) return i++ } else if (void 0 !== spread) { if (rest) rest[k] = x[k] @@ -1205,15 +1207,13 @@ function toMatch(kinds, p) { return } } - return i === n && (!rest || match1(kinds, spread, e, freezeInDev(rest))) + return i === n && (rest ? match1(kinds, spread, e, freezeInDev(rest)) : e) } } } -function toSubst(p, k) { - if (isPayload(k)) { - return void 0 - } else if (void 0 === p || all1(isPrimitive, leafs, p)) { +function toSubst(kinds, p) { + if (void 0 === p || all1(isPrimitive, leafs, p)) { return I.always(p) } else if (isVariable(p)) { const i = p[PAYLOAD][0][PAYLOAD] @@ -1227,42 +1227,69 @@ function toSubst(p, k) { const x = p[i] if (isSpread(x)) { spread = x[PAYLOAD] - } else { + } else if (x !== _) { const side = void 0 !== spread ? rest : init - side.push(toSubst(x)) + side.push(toSubst(kinds, x)) } } return freezeResultInDev(e => { const r = [] - for (let i = 0, n = init[I.LENGTH]; i < n; ++i) pushDefined(r, init[i](e)) + for (let i = 0, n = init[I.LENGTH]; i < n; ++i) { + const v = init[i](e) + if (void 0 === v) return + r.push(v) + } if (0 <= spread) { const xs = e[spread] - if (xs) - for (let i = 0, n = xs[I.LENGTH]; i < n; ++i) pushDefined(r, xs[i]) + if (xs) { + for (let i = 0, n = xs[I.LENGTH]; i < n; ++i) { + r.push(xs[i]) + } + } + } + for (let i = 0, n = rest[I.LENGTH]; i < n; ++i) { + const v = rest[i](e) + if (void 0 === v) return + r.push(v) } - for (let i = 0, n = rest[I.LENGTH]; i < n; ++i) pushDefined(r, rest[i](e)) return r }) } else { let spread = p[PAYLOAD] if (spread) spread = spread[0][PAYLOAD] - p = modify(values, toSubst, p) + p = modify( + values, + (p, k) => { + if (isPayload(k) || _ === p) return + return toSubst(kinds, p) + }, + p + ) return freezeResultInDev(e => { const r = {} - for (const k in p) setDefined(r, k, p[k](e)) + for (const k in p) { + const v = p[k](e) + if (void 0 === v) return + r[k] = v + } if (0 <= spread) { const x = e[spread] - if (x) for (const k in x) setDefined(r, k, x[k]) + if (x) { + for (const k in x) { + r[k] = x[k] + } + } } return r }) } } -const oneway = (n, m, s) => x => { - const e = Array(n) - if (m(e, x)) return s(e) -} +const oneway = (n, m, s) => + function mapping(x) { + const e = m(Array(n), x) + if (e) return s(e) + } // @@ -2270,7 +2297,7 @@ export function mapping(ps) { checkPatternPairInDev(ps) const kinds = Array(n) const ms = ps.map(p => toMatch(kinds, p)) - const ss = ps.map(toSubst) + const ss = ps.map(p => toSubst(kinds, p)) return isoU(oneway(n, ms[0], ss[1]), oneway(n, ms[1], ss[0])) } diff --git a/test/tests.js b/test/tests.js index dd6de854..edf29658 100644 --- a/test/tests.js +++ b/test/tests.js @@ -1479,7 +1479,7 @@ const flatteningRules = L.alternatives( ) ], [ - L.ungroupBy(['ps', L.choices([0, 'id'], [])]), + L.ungroupBy(L.choices(['ps', 0, 'id'], 'id')), L.arrays(L.mapping((o, ps) => [{...o, ps}, {...o, parents: ps}])) ] ) @@ -2539,6 +2539,7 @@ describe('L.mapping', () => { ), {x: 2, y: 1} ) + testEq(() => L.get(L.array(L.mapping(xs => [xs, [...xs]])), [[], {}]), [[]]) }) describe('L.mappings', () => { diff --git a/test/types.js b/test/types.js index e836af56..a8e15ba1 100644 --- a/test/types.js +++ b/test/types.js @@ -411,7 +411,7 @@ export const iso = T.fn( T_isomorphism ) -export const _ = T.any +export const _ = T_pattern export const mapping = T.fn( [T.or(T_patternPair, T.fnVarN(0, T.any, T_patternPair))], From 3e2c84b8502a9f9a9fea0822dfc863b410d67e86 Mon Sep 17 00:00:00 2001 From: Vesa Karvonen Date: Tue, 6 Nov 2018 21:05:36 +0200 Subject: [PATCH 2/9] Added `L.apP` for applying an isomorhism inside a pattern --- src/partial.lenses.js | 35 +++++++++++++++++++++++++++++++++++ test/tests.js | 21 +++++++++++++++++---- test/types.js | 2 ++ 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/partial.lenses.js b/src/partial.lenses.js index e1c891f9..85bd3354 100644 --- a/src/partial.lenses.js +++ b/src/partial.lenses.js @@ -1059,6 +1059,20 @@ function nVars(n) { // +const ISO = 'i' +const PATTERN = 'p' + +function Ap(iso, pattern) { + const self = this + self[ISO] = iso + self[PATTERN] = pattern + I.freeze(self) +} + +const isAp = I.isInstanceOf(Ap) + +// + const isPrimitive = x => { if (x == null) return true const t = typeof x @@ -1098,6 +1112,9 @@ const objectKind = x => void 0 === x || I.isInstanceOf(Object) function checkPattern(kinds, p) { if (isSpread(p)) { throw Error('Spread patterns must be inside objects or arrays.') + } else if (isAp(p)) { + reqOptic(p[ISO]) + checkPattern(kinds, p[PATTERN]) } else if (I.isArray(p)) { let nSpread = 0 for (let i = 0, n = p[I.LENGTH]; i < n; ++i) { @@ -1155,6 +1172,13 @@ function toMatch(kinds, p) { } else if (isVariable(p)) { const i = p[PAYLOAD][0][PAYLOAD] return i < 0 ? id : (e, x) => match1(kinds, i, e, x) + } else if (isAp(p)) { + const m = toMatch(kinds, p[PATTERN]) + const i = p[ISO] + return (e, x) => { + const y = getInverseU(i, x) + return m(e, y) + } } else if (I.isArray(p)) { const init = [] const rest = [] @@ -1218,6 +1242,13 @@ function toSubst(kinds, p) { } else if (isVariable(p)) { const i = p[PAYLOAD][0][PAYLOAD] return e => e[i] + } else if (isAp(p)) { + const s = toSubst(kinds, p[PATTERN]) + const i = p[ISO] + return e => { + const x = s(e) + return getU(i, x) + } } else if (I.isArray(p)) { const init = [] const rest = [] @@ -2291,6 +2322,10 @@ export const iso = I.curry(isoU) export const _ = new Variable(-1) +export const apP = I.curry(function apP(iso, pattern) { + return new Ap(iso, pattern) +}) + export function mapping(ps) { let n = 0 if (I.isFunction(ps)) ps = ps.apply(null, nVars((n = ps[I.LENGTH]))) diff --git a/test/tests.js b/test/tests.js index edf29658..24db9994 100644 --- a/test/tests.js +++ b/test/tests.js @@ -231,6 +231,7 @@ describe('arities', () => { and1: 2, and: 2, any: 3, + apP: 2, append: 4, appendOp: 1, appendTo: 4, @@ -2388,10 +2389,8 @@ describe('L.fold', () => { }) describe('L.iterate', () => { - const step = abIa => [ - L.mapping((a, b, bs) => [[a, [b, ...bs]], [[a, b], bs]]), - L.cross([abIa, L.identity]) - ] + const step = abIa => + L.mapping((a, b, bs) => [[a, [b, ...bs]], [L.apP(abIa, [a, b]), bs]]) const foldl = abIa => [L.iterate(step(abIa)), L.mapping(a => [[a, []], a])] testEq( () => @@ -2417,6 +2416,20 @@ describe('L.patterns', () => { }) describe('L.mapping', () => { + testEq( + () => + L.get( + L.applyAt( + L.values, + L.mapping((x, y) => [ + [x, L.apP(L.mapping(x => [x, [x, x]]), y)], + [x, y] + ]) + ), + {a: [1, 2], b: [3, [4, 4]]} + ), + {b: [3, 4]} + ) testEq( () => L.get(L.mapping((x, y) => [[x, L._, ...L._, y], [y, ...L._, x]]), [ diff --git a/test/types.js b/test/types.js index a8e15ba1..9839edff 100644 --- a/test/types.js +++ b/test/types.js @@ -413,6 +413,8 @@ export const iso = T.fn( export const _ = T_pattern +export const apP = T.fn([T_isomorphism, T_pattern], T_pattern) + export const mapping = T.fn( [T.or(T_patternPair, T.fnVarN(0, T.any, T_patternPair))], T_isomorphism From ea0e58ca3f335ee98f1bd2a098b6ba252121b85d Mon Sep 17 00:00:00 2001 From: Vesa Karvonen Date: Tue, 6 Nov 2018 21:06:31 +0200 Subject: [PATCH 3/9] Added `L.letP` for alternating between matching and substition --- src/partial.lenses.js | 52 +++++++++++++++++++++++++++++++++++++++++++ test/ppp.js | 9 ++++---- test/tests.js | 5 +++++ test/types.js | 2 ++ 4 files changed, 64 insertions(+), 4 deletions(-) diff --git a/src/partial.lenses.js b/src/partial.lenses.js index 85bd3354..b86e3b7b 100644 --- a/src/partial.lenses.js +++ b/src/partial.lenses.js @@ -1073,6 +1073,22 @@ const isAp = I.isInstanceOf(Ap) // +const TO = 't' +const FROM = 'f' +const BODY = 'b' + +function Let(to, from, body) { + const self = this + self[TO] = to + self[FROM] = from + self[BODY] = body + I.freeze(self) +} + +const isLet = I.isInstanceOf(Let) + +// + const isPrimitive = x => { if (x == null) return true const t = typeof x @@ -1115,6 +1131,10 @@ function checkPattern(kinds, p) { } else if (isAp(p)) { reqOptic(p[ISO]) checkPattern(kinds, p[PATTERN]) + } else if (isLet(p)) { + checkPattern(kinds, p[TO]) + checkPattern(kinds, p[FROM]) + checkPattern(kinds, p[BODY]) } else if (I.isArray(p)) { let nSpread = 0 for (let i = 0, n = p[I.LENGTH]; i < n; ++i) { @@ -1172,6 +1192,17 @@ function toMatch(kinds, p) { } else if (isVariable(p)) { const i = p[PAYLOAD][0][PAYLOAD] return i < 0 ? id : (e, x) => match1(kinds, i, e, x) + } else if (isLet(p)) { + const b = toMatch(kinds, p[BODY]) + const t = toSubst(kinds, p[TO]) + const f = toMatch(kinds, p[FROM]) + return (e, x) => { + e = b(e, x) + if (void 0 === e) return + const tv = t(e) + if (void 0 === e) return + return f(e, tv) + } } else if (isAp(p)) { const m = toMatch(kinds, p[PATTERN]) const i = p[ISO] @@ -1242,6 +1273,17 @@ function toSubst(kinds, p) { } else if (isVariable(p)) { const i = p[PAYLOAD][0][PAYLOAD] return e => e[i] + } else if (isLet(p)) { + const f = toSubst(kinds, p[FROM]) + const t = toMatch(kinds, p[TO]) + const b = toSubst(kinds, p[BODY]) + return e => { + const fv = f(e) + if (void 0 === fv) return + e = t(e, fv) + if (void 0 === e) return + return b(e) + } } else if (isAp(p)) { const s = toSubst(kinds, p[PATTERN]) const i = p[ISO] @@ -2326,6 +2368,16 @@ export const apP = I.curry(function apP(iso, pattern) { return new Ap(iso, pattern) }) +export const letP = function letP(_binder, _pattern) { + let n = arguments[I.LENGTH] - 1 + let p = arguments[n] + while (n--) { + const b = arguments[n] + p = new Let(b[0], b[1], p) + } + return p +} + export function mapping(ps) { let n = 0 if (I.isFunction(ps)) ps = ps.apply(null, nVars((n = ps[I.LENGTH]))) diff --git a/test/ppp.js b/test/ppp.js index 0048f7d4..9735191b 100644 --- a/test/ppp.js +++ b/test/ppp.js @@ -31,10 +31,11 @@ export const isomap = R.curry((iso, ppp) => [ppp, L.cross([[], iso])]) const empty = of([]) -const append = piso => [ - L.cross([piso, []]), - L.mapping((xs, y, s) => [[[s, y], xs], [s, [...xs, y]]]) -] +const append = piso => + L.mapping((xs, y, s1, s2) => [ + [s1, xs], + L.letP([[s2, y], L.apP(piso, s1)], [s2, [...xs, y]]) + ]) export const sequence = (...pisos) => [empty, pisos.map(append)] diff --git a/test/tests.js b/test/tests.js index 24db9994..049af87e 100644 --- a/test/tests.js +++ b/test/tests.js @@ -321,6 +321,7 @@ describe('arities', () => { lazy: 1, leafs: 4, lens: 2, + letP: 2, limit: 2, log: 0, mapIx: 1, @@ -2439,6 +2440,10 @@ describe('L.mapping', () => { ]), [3, 1] ) + testEq( + () => L.get(L.mapping((x, y) => [x, L.letP([[y], x], y)]), 1), + undefined + ) testEq(() => L.get(L.mapping(xs => [xs, [[...xs], [...xs]]]), [1, 2]), [ [1, 2], [1, 2] diff --git a/test/types.js b/test/types.js index 9839edff..7634ca3e 100644 --- a/test/types.js +++ b/test/types.js @@ -415,6 +415,8 @@ export const _ = T_pattern export const apP = T.fn([T_isomorphism, T_pattern], T_pattern) +export const letP = T.fnVarN(2, T.any, T_pattern) + export const mapping = T.fn( [T.or(T_patternPair, T.fnVarN(0, T.any, T_patternPair))], T_isomorphism From e0369e0cdd94c44adb0aef1943135c5d678d30bf Mon Sep 17 00:00:00 2001 From: Vesa Karvonen Date: Wed, 7 Nov 2018 09:20:49 +0200 Subject: [PATCH 4/9] Refactored pattern matching into modular handlers --- src/partial.lenses.js | 522 +++++++++++++++++++++++------------------- 1 file changed, 282 insertions(+), 240 deletions(-) diff --git a/src/partial.lenses.js b/src/partial.lenses.js index b86e3b7b..cede0daa 100644 --- a/src/partial.lenses.js +++ b/src/partial.lenses.js @@ -4,6 +4,8 @@ import * as C from './contract' // +const ignore = _ => {} + const id = x => x const setName = process.env.NODE_ENV === 'production' ? x => x : I.defineNameU @@ -1028,6 +1030,38 @@ const foldPartial = sa2s => sxs => { // +function match1(kinds, i, e, x) { + if (void 0 !== x) { + const previous = e[i] + if (undefined !== previous) { + if (I.acyclicEqualsU(e[i], x)) return e + } else { + const k = kinds[i] + if (!k || k(x)) { + return setIndex(i, x, e) + } + } + } +} + +function checkKind(kinds, i, kind) { + if (0 <= i) { + if (kinds[i]) { + if (kinds[i] !== kind) + throw Error( + 'Spread patterns must be used consistently either as arrays or as objects.' + ) + } else { + kinds[i] = kind + } + } +} + +const arrayKind = x => void 0 === x || I.isArray(x) +const objectKind = x => void 0 === x || I.isInstanceOf(Object) + +// + const PAYLOAD = '珳襱댎纚䤤鬖罺좴' const isPayload = k => I.isString(k) && k.indexOf(PAYLOAD) === 0 @@ -1039,6 +1073,21 @@ function Spread(i) { const isSpread = I.isInstanceOf(Spread) +// + +const isPrimitive = x => { + if (x == null) return true + const t = typeof x + return t !== 'object' && t !== 'function' +} + +const tVal = p => void 0 === p || all1(isPrimitive, leafs, p) +const cVal = ignore +const mVal = p => (e, x) => I.acyclicEqualsU(p, x) && e +const sVal = I.always + +// + const Variable = I.inherit( function Variable(i) { this[PAYLOAD + i] = this[PAYLOAD] = I.freeze([new Spread(i)]) @@ -1049,7 +1098,6 @@ const Variable = I.inherit( return this[PAYLOAD][I.iterator]() }) ) -const isVariable = I.isInstanceOf(Variable) const vars = [] function nVars(n) { @@ -1057,6 +1105,17 @@ function nVars(n) { return vars } +const tVar = I.isInstanceOf(Variable) +const cVar = ignore +const mVar = (p, kinds) => { + const i = p[PAYLOAD][0][PAYLOAD] + return i < 0 ? id : (e, x) => match1(kinds, i, e, x) +} +const sVar = p => { + const i = p[PAYLOAD][0][PAYLOAD] + return e => e[i] +} + // const ISO = 'i' @@ -1069,7 +1128,27 @@ function Ap(iso, pattern) { I.freeze(self) } -const isAp = I.isInstanceOf(Ap) +const tAp = I.isInstanceOf(Ap) +const cAp = (p, kinds, check) => { + reqOptic(p[ISO]) + check(p[PATTERN], kinds) +} +const mAp = (p, kinds, match) => { + const m = match(p[PATTERN], kinds) + const i = p[ISO] + return (e, x) => { + const y = getInverseU(i, x) + return m(e, y) + } +} +const sAp = (p, kinds, subst) => { + const s = subst(p[PATTERN], kinds) + const i = p[ISO] + return e => { + const x = s(e) + return getU(i, x) + } +} // @@ -1085,94 +1164,229 @@ function Let(to, from, body) { I.freeze(self) } -const isLet = I.isInstanceOf(Let) +const tLet = I.isInstanceOf(Let) +const cLet = (p, kinds, check) => { + check(p[TO], kinds) + check(p[FROM], kinds) + check(p[BODY], kinds) +} +const mLet = (p, kinds, match, subst) => { + const bM = match(p[BODY], kinds) + const tS = subst(p[TO], kinds) + const fM = match(p[FROM], kinds) + return (e, x) => { + e = bM(e, x) + if (e) { + const tv = tS(e) + return fM(e, tv) + } + } +} +const sLet = (p, kinds, subst, match) => { + const fS = subst(p[FROM], kinds) + const tM = match(p[TO], kinds) + const bS = subst(p[BODY], kinds) + return e => { + const fv = fS(e) + if (void 0 !== fv) { + e = tM(e, fv) + if (e) return bS(e) + } + } +} // -const isPrimitive = x => { - if (x == null) return true - const t = typeof x - return t !== 'object' && t !== 'function' -} - -function match1(kinds, i, e, x) { - if (void 0 !== x) { - const previous = e[i] - if (undefined !== previous) { - if (I.acyclicEqualsU(e[i], x)) return e +const tArr = I.isArray +const cArr = (p, kinds, check) => { + let nSpread = 0 + for (let i = 0, n = p[I.LENGTH]; i < n; ++i) { + const pi = p[i] + if (isSpread(pi)) { + if (nSpread++) + throw Error('At most one spread is allowed in an array or object.') + checkKind(kinds, pi[PAYLOAD], arrayKind) } else { - const k = kinds[i] - if (!k || k(x)) { - return setIndex(i, x, e) - } + check(pi, kinds) } } } - -function checkKind(kinds, i, kind) { - if (0 <= i) { - if (kinds[i]) { - if (kinds[i] !== kind) - throw Error( - 'Spread patterns must be used consistently either as arrays or as objects.' - ) +const mArr = (p, kinds, match) => { + const init = [] + const rest = [] + let spread = void 0 + const n = p[I.LENGTH] + for (let i = 0; i < n; ++i) { + const x = p[i] + if (isSpread(x)) { + spread = x[PAYLOAD] + kinds[spread] = arrayKind } else { - kinds[i] = kind + const side = void 0 !== spread ? rest : init + side.push(match(x, kinds)) } } + return (e, x) => { + if (!seemsArrayLike(x)) return + let l = x[I.LENGTH] + if (void 0 !== spread ? l < n - 1 : l !== n) return + const j = init[I.LENGTH] + for (let i = 0; i < j; ++i) if (!(e = init[i](e, x[i]))) return + const k = rest[I.LENGTH] + l -= k + for (let i = 0; i < k; ++i) if (!(e = rest[i](e, x[l + i]))) return + return 0 <= spread + ? match1(kinds, spread, e, copyToFrom(Array(l - j), 0, x, j, l)) + : e + } +} +const sArr = (p, kinds, subst) => { + const init = [] + const rest = [] + let spread = void 0 + const n = p[I.LENGTH] + for (let i = 0; i < n; ++i) { + const x = p[i] + if (isSpread(x)) { + spread = x[PAYLOAD] + } else if (x !== _) { + const side = void 0 !== spread ? rest : init + side.push(subst(x, kinds)) + } + } + return freezeResultInDev(e => { + const r = [] + for (let i = 0, n = init[I.LENGTH]; i < n; ++i) { + const v = init[i](e) + if (void 0 === v) return + r.push(v) + } + if (0 <= spread) { + const xs = e[spread] + if (xs) { + for (let i = 0, n = xs[I.LENGTH]; i < n; ++i) { + r.push(xs[i]) + } + } + } + for (let i = 0, n = rest[I.LENGTH]; i < n; ++i) { + const v = rest[i](e) + if (void 0 === v) return + r.push(v) + } + return r + }) } -const arrayKind = x => void 0 === x || I.isArray(x) -const objectKind = x => void 0 === x || I.isInstanceOf(Object) +// -function checkPattern(kinds, p) { - if (isSpread(p)) { - throw Error('Spread patterns must be inside objects or arrays.') - } else if (isAp(p)) { - reqOptic(p[ISO]) - checkPattern(kinds, p[PATTERN]) - } else if (isLet(p)) { - checkPattern(kinds, p[TO]) - checkPattern(kinds, p[FROM]) - checkPattern(kinds, p[BODY]) - } else if (I.isArray(p)) { - let nSpread = 0 - for (let i = 0, n = p[I.LENGTH]; i < n; ++i) { - const pi = p[i] - if (isSpread(pi)) { - if (nSpread++) - throw Error('At most one spread is allowed in an array or object.') - checkKind(kinds, pi[PAYLOAD], arrayKind) +const tObj = I.isObject +const cObj = (p, kinds, check) => { + let spread = p[PAYLOAD] + if (spread) { + spread = spread[0][PAYLOAD] + checkKind(kinds, spread, objectKind) + } + let n = 0 + for (const k in p) { + if (isPayload(k)) { + if (2 < ++n) + throw Error('At most one spread is allowed in an array or object.') + } else { + check(p[k], kinds) + } + } +} +const mObj = (p, kinds, match) => { + let spread = p[PAYLOAD] + if (spread) { + spread = spread[0][PAYLOAD] + kinds[spread] = objectKind + } + p = modify(values, (p, k) => (isPayload(k) ? void 0 : match(p, kinds)), p) + const n = count(values, p) + return (e, x) => { + if (isPrimitive(x) || I.isArray(x)) return + x = toObject(x) + const rest = 0 <= spread && {} + let i = 0 + for (const k in x) { + const m = p[k] + if (m) { + if (!(e = m(e, x[k]))) return + i++ + } else if (void 0 !== spread) { + if (rest) rest[k] = x[k] } else { - checkPattern(kinds, pi) + return } } - } else if (I.isObject(p)) { - let spread = p[PAYLOAD] - if (spread) { - spread = spread[0][PAYLOAD] - checkKind(kinds, spread, objectKind) - } - let n = 0 + return i === n && (rest ? match1(kinds, spread, e, freezeInDev(rest)) : e) + } +} +const sObj = (p, kinds, subst) => { + let spread = p[PAYLOAD] + if (spread) spread = spread[0][PAYLOAD] + p = modify( + values, + (p, k) => { + if (isPayload(k) || _ === p) return + return subst(p, kinds) + }, + p + ) + return freezeResultInDev(e => { + const r = {} for (const k in p) { - if (isPayload(k)) { - if (2 < ++n) - throw Error('At most one spread is allowed in an array or object.') - } else { - checkPattern(kinds, p[k]) + const v = p[k](e) + if (void 0 === v) return + r[k] = v + } + if (0 <= spread) { + const x = e[spread] + if (x) { + for (const k in x) { + r[k] = x[k] + } } } - } else if (!isPrimitive(p) && !isVariable(p)) { - throw Error('Only plain arrays and objects are allowed in patterns.') + return r + }) +} + +// + +const tsts = [tVal, tVar, tAp, tLet, tArr, tObj] +const chks = [cVal, cVar, cAp, cLet, cArr, cObj] +const mchs = [mVal, mVar, mAp, mLet, mArr, mObj] +const subs = [sVal, sVar, sAp, sLet, sArr, sObj] + +const idxOf = p => { + for (let i = 0, n = tsts[I.LENGTH]; i < n; ++i) { + const t = tsts[i] + if (t(p)) return i } + throw Error(`Unrecognized pattern: ${p}`) } +const chkPattern = (p, kinds) => { + chks[idxOf(p)](p, kinds, chkPattern) +} + +const mchPattern = (p, kinds) => + mchs[idxOf(p)](p, kinds, mchPattern, subPattern) + +const subPattern = (p, kinds) => + subs[idxOf(p)](p, kinds, subPattern, mchPattern) + +// + const checkPatternInDev = process.env.NODE_ENV === 'production' ? id : p => { const kinds = [] - checkPattern(kinds, p) + chkPattern(p, kinds) return deepFreezeInDev(p) } @@ -1181,183 +1395,11 @@ const checkPatternPairInDev = ? id : ps => { const kinds = [] - checkPattern(kinds, ps[0]) - checkPattern(kinds, ps[1]) + chkPattern(ps[0], kinds) + chkPattern(ps[1], kinds) return deepFreezeInDev(ps) } -function toMatch(kinds, p) { - if (void 0 === p || all1(isPrimitive, leafs, p)) { - return (e, x) => I.acyclicEqualsU(p, x) && e - } else if (isVariable(p)) { - const i = p[PAYLOAD][0][PAYLOAD] - return i < 0 ? id : (e, x) => match1(kinds, i, e, x) - } else if (isLet(p)) { - const b = toMatch(kinds, p[BODY]) - const t = toSubst(kinds, p[TO]) - const f = toMatch(kinds, p[FROM]) - return (e, x) => { - e = b(e, x) - if (void 0 === e) return - const tv = t(e) - if (void 0 === e) return - return f(e, tv) - } - } else if (isAp(p)) { - const m = toMatch(kinds, p[PATTERN]) - const i = p[ISO] - return (e, x) => { - const y = getInverseU(i, x) - return m(e, y) - } - } else if (I.isArray(p)) { - const init = [] - const rest = [] - let spread = void 0 - const n = p[I.LENGTH] - for (let i = 0; i < n; ++i) { - const x = p[i] - if (isSpread(x)) { - spread = x[PAYLOAD] - kinds[spread] = arrayKind - } else { - const side = void 0 !== spread ? rest : init - side.push(toMatch(kinds, x)) - } - } - return (e, x) => { - if (!seemsArrayLike(x)) return - let l = x[I.LENGTH] - if (void 0 !== spread ? l < n - 1 : l !== n) return - const j = init[I.LENGTH] - for (let i = 0; i < j; ++i) if (!(e = init[i](e, x[i]))) return - const k = rest[I.LENGTH] - l -= k - for (let i = 0; i < k; ++i) if (!(e = rest[i](e, x[l + i]))) return - return 0 <= spread - ? match1(kinds, spread, e, copyToFrom(Array(l - j), 0, x, j, l)) - : e - } - } else { - let spread = p[PAYLOAD] - if (spread) { - spread = spread[0][PAYLOAD] - kinds[spread] = objectKind - } - p = modify(values, (p, k) => (isPayload(k) ? void 0 : toMatch(kinds, p)), p) - const n = count(values, p) - return (e, x) => { - if (isPrimitive(x) || I.isArray(x)) return - x = toObject(x) - const rest = 0 <= spread && {} - let i = 0 - for (const k in x) { - const m = p[k] - if (m) { - if (!(e = m(e, x[k]))) return - i++ - } else if (void 0 !== spread) { - if (rest) rest[k] = x[k] - } else { - return - } - } - return i === n && (rest ? match1(kinds, spread, e, freezeInDev(rest)) : e) - } - } -} - -function toSubst(kinds, p) { - if (void 0 === p || all1(isPrimitive, leafs, p)) { - return I.always(p) - } else if (isVariable(p)) { - const i = p[PAYLOAD][0][PAYLOAD] - return e => e[i] - } else if (isLet(p)) { - const f = toSubst(kinds, p[FROM]) - const t = toMatch(kinds, p[TO]) - const b = toSubst(kinds, p[BODY]) - return e => { - const fv = f(e) - if (void 0 === fv) return - e = t(e, fv) - if (void 0 === e) return - return b(e) - } - } else if (isAp(p)) { - const s = toSubst(kinds, p[PATTERN]) - const i = p[ISO] - return e => { - const x = s(e) - return getU(i, x) - } - } else if (I.isArray(p)) { - const init = [] - const rest = [] - let spread = void 0 - const n = p[I.LENGTH] - for (let i = 0; i < n; ++i) { - const x = p[i] - if (isSpread(x)) { - spread = x[PAYLOAD] - } else if (x !== _) { - const side = void 0 !== spread ? rest : init - side.push(toSubst(kinds, x)) - } - } - return freezeResultInDev(e => { - const r = [] - for (let i = 0, n = init[I.LENGTH]; i < n; ++i) { - const v = init[i](e) - if (void 0 === v) return - r.push(v) - } - if (0 <= spread) { - const xs = e[spread] - if (xs) { - for (let i = 0, n = xs[I.LENGTH]; i < n; ++i) { - r.push(xs[i]) - } - } - } - for (let i = 0, n = rest[I.LENGTH]; i < n; ++i) { - const v = rest[i](e) - if (void 0 === v) return - r.push(v) - } - return r - }) - } else { - let spread = p[PAYLOAD] - if (spread) spread = spread[0][PAYLOAD] - p = modify( - values, - (p, k) => { - if (isPayload(k) || _ === p) return - return toSubst(kinds, p) - }, - p - ) - return freezeResultInDev(e => { - const r = {} - for (const k in p) { - const v = p[k](e) - if (void 0 === v) return - r[k] = v - } - if (0 <= spread) { - const x = e[spread] - if (x) { - for (const k in x) { - r[k] = x[k] - } - } - } - return r - }) - } -} - const oneway = (n, m, s) => function mapping(x) { const e = m(Array(n), x) @@ -2383,8 +2425,8 @@ export function mapping(ps) { if (I.isFunction(ps)) ps = ps.apply(null, nVars((n = ps[I.LENGTH]))) checkPatternPairInDev(ps) const kinds = Array(n) - const ms = ps.map(p => toMatch(kinds, p)) - const ss = ps.map(p => toSubst(kinds, p)) + const ms = ps.map(p => mchPattern(p, kinds)) + const ss = ps.map(p => subPattern(p, kinds)) return isoU(oneway(n, ms[0], ss[1]), oneway(n, ms[1], ss[0])) } @@ -2398,7 +2440,7 @@ export function pattern(p) { if (I.isFunction(p)) p = p.apply(null, nVars((n = p[I.LENGTH]))) checkPatternInDev(p) const kinds = Array(n) - const m = toMatch(kinds, p) + const m = mchPattern(p, kinds) return subset(x => m(Array(n), x)) } From 546db7fd4e4988f6c8115b151f804a3c7a6524d2 Mon Sep 17 00:00:00 2001 From: Vesa Karvonen Date: Wed, 7 Nov 2018 20:50:01 +0200 Subject: [PATCH 5/9] Added `L.andP` --- src/partial.lenses.js | 57 ++++++++++++++++++++++++++++++++++++++++--- test/tests.js | 13 ++++++++++ test/types.js | 2 ++ 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/src/partial.lenses.js b/src/partial.lenses.js index cede0daa..e8827e17 100644 --- a/src/partial.lenses.js +++ b/src/partial.lenses.js @@ -1118,6 +1118,45 @@ const sVar = p => { // +const LHS = 'l' +const RHS = 'r' + +function And(lhs, rhs) { + const self = this + self[LHS] = lhs + self[RHS] = rhs + I.freeze(self) +} + +const tAnd = I.isInstanceOf(And) +const cAnd = (p, kinds, check) => { + check(p[LHS], kinds) + check(p[RHS], kinds) +} +const mAnd = (p, kinds, match) => { + const lM = match(p[LHS], kinds) + const rM = match(p[RHS], kinds) + return (e, x) => { + e = lM(e, x) + if (e) e = rM(e, x) + return e + } +} +const sAnd = (p, kinds, subst, match) => { + const lM = match(p[LHS], kinds) + const rM = match(p[RHS], kinds) + const lS = subst(p[LHS], kinds) + const rS = subst(p[RHS], kinds) + return e => { + const l = lS(e) + if (rM(e, l)) return l + const r = rS(e) + if (lM(e, r)) return r + } +} + +// + const ISO = 'i' const PATTERN = 'p' @@ -1356,10 +1395,10 @@ const sObj = (p, kinds, subst) => { // -const tsts = [tVal, tVar, tAp, tLet, tArr, tObj] -const chks = [cVal, cVar, cAp, cLet, cArr, cObj] -const mchs = [mVal, mVar, mAp, mLet, mArr, mObj] -const subs = [sVal, sVar, sAp, sLet, sArr, sObj] +const tsts = [tVal, tAnd, tVar, tAp, tLet, tArr, tObj] +const chks = [cVal, cAnd, cVar, cAp, cLet, cArr, cObj] +const mchs = [mVal, mAnd, mVar, mAp, mLet, mArr, mObj] +const subs = [sVal, sAnd, sVar, sAp, sLet, sArr, sObj] const idxOf = p => { for (let i = 0, n = tsts[I.LENGTH]; i < n; ++i) { @@ -2406,6 +2445,16 @@ export const iso = I.curry(isoU) export const _ = new Variable(-1) +export const andP = function andP() { + let p = toTrue + let n = arguments[I.LENGTH] + if (n) { + p = arguments[--n] + while (n--) p = new And(arguments[n], p) + } + return p +} + export const apP = I.curry(function apP(iso, pattern) { return new Ap(iso, pattern) }) diff --git a/test/tests.js b/test/tests.js index 049af87e..3ba75509 100644 --- a/test/tests.js +++ b/test/tests.js @@ -230,6 +230,7 @@ describe('arities', () => { alternatives: 1, and1: 2, and: 2, + andP: 0, any: 3, apP: 2, append: 4, @@ -2417,6 +2418,18 @@ describe('L.patterns', () => { }) describe('L.mapping', () => { + testEq( + () => L.get(L.array(L.mapping(x => [L.andP({...L._}, x), x])), [{}, []]), + [{}] + ) + testEq( + () => + L.getInverse(L.array(L.mapping((x, y) => [L.andP(x, {...L._}, y), y])), [ + {}, + [] + ]), + [{}] + ) testEq( () => L.get( diff --git a/test/types.js b/test/types.js index 7634ca3e..65ef8029 100644 --- a/test/types.js +++ b/test/types.js @@ -413,6 +413,8 @@ export const iso = T.fn( export const _ = T_pattern +export const andP = T.fnVarN(0, T_pattern, T_pattern) + export const apP = T.fn([T_isomorphism, T_pattern], T_pattern) export const letP = T.fnVarN(2, T.any, T_pattern) From 99fc4f0a9590174f12f5b02077c44fe7b08402b0 Mon Sep 17 00:00:00 2001 From: Vesa Karvonen Date: Wed, 7 Nov 2018 20:51:27 +0200 Subject: [PATCH 6/9] Added `L.orP` --- src/partial.lenses.js | 48 +++++++++++++++++++++++++++++++++++++++---- test/tests.js | 18 ++++++++++++++++ test/types.js | 2 ++ 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/src/partial.lenses.js b/src/partial.lenses.js index e8827e17..7653d02a 100644 --- a/src/partial.lenses.js +++ b/src/partial.lenses.js @@ -1157,6 +1157,36 @@ const sAnd = (p, kinds, subst, match) => { // +function Or(lhs, rhs) { + const self = this + self[LHS] = lhs + self[RHS] = rhs + I.freeze(self) +} + +const tOr = I.isInstanceOf(Or) +const cOr = (p, kinds, check) => { + check(p[LHS], kinds) + check(p[RHS], kinds) +} +const mOr = (p, kinds, match) => { + const lM = match(p[LHS], kinds) + const rM = match(p[RHS], kinds) + return (e, x) => lM(e, x) || rM(e, x) +} +const sOr = (p, kinds, subst) => { + const lS = subst(p[LHS], kinds) + const rS = subst(p[RHS], kinds) + return e => { + const l = lS(e) + if (void 0 !== l) return l + const r = rS(e) + if (void 0 !== r) return r + } +} + +// + const ISO = 'i' const PATTERN = 'p' @@ -1395,10 +1425,10 @@ const sObj = (p, kinds, subst) => { // -const tsts = [tVal, tAnd, tVar, tAp, tLet, tArr, tObj] -const chks = [cVal, cAnd, cVar, cAp, cLet, cArr, cObj] -const mchs = [mVal, mAnd, mVar, mAp, mLet, mArr, mObj] -const subs = [sVal, sAnd, sVar, sAp, sLet, sArr, sObj] +const tsts = [tVal, tAnd, tOr, tVar, tAp, tLet, tArr, tObj] +const chks = [cVal, cAnd, cOr, cVar, cAp, cLet, cArr, cObj] +const mchs = [mVal, mAnd, mOr, mVar, mAp, mLet, mArr, mObj] +const subs = [sVal, sAnd, sOr, sVar, sAp, sLet, sArr, sObj] const idxOf = p => { for (let i = 0, n = tsts[I.LENGTH]; i < n; ++i) { @@ -2469,6 +2499,16 @@ export const letP = function letP(_binder, _pattern) { return p } +export const orP = function orP() { + let p = ignore + let n = arguments[I.LENGTH] + if (n) { + p = arguments[--n] + while (n--) p = new Or(arguments[n], p) + } + return p +} + export function mapping(ps) { let n = 0 if (I.isFunction(ps)) ps = ps.apply(null, nVars((n = ps[I.LENGTH]))) diff --git a/test/tests.js b/test/tests.js index 3ba75509..0ef443f0 100644 --- a/test/tests.js +++ b/test/tests.js @@ -348,6 +348,7 @@ describe('arities', () => { or: 2, orAlternatively: 2, orElse: 2, + orP: 0, partsOf: 1, pattern: 1, patterns: 1, @@ -2418,6 +2419,23 @@ describe('L.patterns', () => { }) describe('L.mapping', () => { + testEq( + () => + L.get(L.array(L.mapping(x => [L.orP(L.andP(x, []), L.andP(x, {})), x])), [ + {}, + 1, + [] + ]), + [{}, []] + ) + testEq( + () => + L.getInverse( + L.array(L.mapping(x => [L.orP(L.andP(x, []), L.andP(x, {})), x])), + [{}, 1, []] + ), + [{}, []] + ) testEq( () => L.get(L.array(L.mapping(x => [L.andP({...L._}, x), x])), [{}, []]), [{}] diff --git a/test/types.js b/test/types.js index 65ef8029..78204ac8 100644 --- a/test/types.js +++ b/test/types.js @@ -419,6 +419,8 @@ export const apP = T.fn([T_isomorphism, T_pattern], T_pattern) export const letP = T.fnVarN(2, T.any, T_pattern) +export const orP = T.fnVarN(0, T_pattern, T_pattern) + export const mapping = T.fn( [T.or(T_patternPair, T.fnVarN(0, T.any, T_patternPair))], T_isomorphism From c1f1d42fc777620ddc1ab0763046f46d8cba00c8 Mon Sep 17 00:00:00 2001 From: Vesa Karvonen Date: Wed, 7 Nov 2018 20:52:50 +0200 Subject: [PATCH 7/9] Added `L.notP` --- src/partial.lenses.js | 33 +++++++++++++++++++++++++++++---- test/tests.js | 6 ++++++ test/types.js | 2 ++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/partial.lenses.js b/src/partial.lenses.js index 7653d02a..f13307ba 100644 --- a/src/partial.lenses.js +++ b/src/partial.lenses.js @@ -1187,6 +1187,27 @@ const sOr = (p, kinds, subst) => { // +const PAT = 'p' + +function Not(pat) { + this[PAT] = pat + I.freeze(this) +} + +const tNot = I.isInstanceOf(Not) +const cNot = (p, kinds, check) => { + check(p[PAT]) +} +const mNot = (p, kinds, match) => { + const m = match(p[PAT], kinds) + return (e, x) => { + if (!m(e, x)) return e + } +} +const sNot = I.always(ignore) + +// + const ISO = 'i' const PATTERN = 'p' @@ -1425,10 +1446,10 @@ const sObj = (p, kinds, subst) => { // -const tsts = [tVal, tAnd, tOr, tVar, tAp, tLet, tArr, tObj] -const chks = [cVal, cAnd, cOr, cVar, cAp, cLet, cArr, cObj] -const mchs = [mVal, mAnd, mOr, mVar, mAp, mLet, mArr, mObj] -const subs = [sVal, sAnd, sOr, sVar, sAp, sLet, sArr, sObj] +const tsts = [tVal, tAnd, tOr, tNot, tVar, tAp, tLet, tArr, tObj] +const chks = [cVal, cAnd, cOr, cNot, cVar, cAp, cLet, cArr, cObj] +const mchs = [mVal, mAnd, mOr, mNot, mVar, mAp, mLet, mArr, mObj] +const subs = [sVal, sAnd, sOr, sNot, sVar, sAp, sLet, sArr, sObj] const idxOf = p => { for (let i = 0, n = tsts[I.LENGTH]; i < n; ++i) { @@ -2499,6 +2520,10 @@ export const letP = function letP(_binder, _pattern) { return p } +export const notP = function notP(p) { + return new Not(p) +} + export const orP = function orP() { let p = ignore let n = arguments[I.LENGTH] diff --git a/test/tests.js b/test/tests.js index 0ef443f0..71cb6c78 100644 --- a/test/tests.js +++ b/test/tests.js @@ -343,6 +343,7 @@ describe('arities', () => { negate: 4, none: 3, normalize: 1, + notP: 1, offset: 2, optional: 4, or: 2, @@ -2419,6 +2420,11 @@ describe('L.patterns', () => { }) describe('L.mapping', () => { + testEq( + () => + L.get(L.array(L.mapping(x => [L.andP(L.notP([]), x), x])), [{}, 1, []]), + [{}, 1] + ) testEq( () => L.get(L.array(L.mapping(x => [L.orP(L.andP(x, []), L.andP(x, {})), x])), [ diff --git a/test/types.js b/test/types.js index 78204ac8..118a612c 100644 --- a/test/types.js +++ b/test/types.js @@ -419,6 +419,8 @@ export const apP = T.fn([T_isomorphism, T_pattern], T_pattern) export const letP = T.fnVarN(2, T.any, T_pattern) +export const notP = T.fn([T_pattern], T_pattern) + export const orP = T.fnVarN(0, T_pattern, T_pattern) export const mapping = T.fn( From f278c4ef554aadb9f2dabf503381521aa74ac1a4 Mon Sep 17 00:00:00 2001 From: Vesa Karvonen Date: Wed, 7 Nov 2018 20:53:09 +0200 Subject: [PATCH 8/9] Added predicate patterns --- src/partial.lenses.js | 17 +++++++++++++---- test/tests.js | 9 +++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/partial.lenses.js b/src/partial.lenses.js index f13307ba..701449b1 100644 --- a/src/partial.lenses.js +++ b/src/partial.lenses.js @@ -1208,6 +1208,15 @@ const sNot = I.always(ignore) // +const tPr = I.isFunction +const cPr = p => { + if (p[I.LENGTH] !== 1) throw Error(`Predicates should be unary`) +} +const mPr = p => (e, x) => p(x) && e +const sPr = I.always(ignore) + +// + const ISO = 'i' const PATTERN = 'p' @@ -1446,10 +1455,10 @@ const sObj = (p, kinds, subst) => { // -const tsts = [tVal, tAnd, tOr, tNot, tVar, tAp, tLet, tArr, tObj] -const chks = [cVal, cAnd, cOr, cNot, cVar, cAp, cLet, cArr, cObj] -const mchs = [mVal, mAnd, mOr, mNot, mVar, mAp, mLet, mArr, mObj] -const subs = [sVal, sAnd, sOr, sNot, sVar, sAp, sLet, sArr, sObj] +const tsts = [tVal, tAnd, tOr, tNot, tPr, tVar, tAp, tLet, tArr, tObj] +const chks = [cVal, cAnd, cOr, cNot, cPr, cVar, cAp, cLet, cArr, cObj] +const mchs = [mVal, mAnd, mOr, mNot, mPr, mVar, mAp, mLet, mArr, mObj] +const subs = [sVal, sAnd, sOr, sNot, sPr, sVar, sAp, sLet, sArr, sObj] const idxOf = p => { for (let i = 0, n = tsts[I.LENGTH]; i < n; ++i) { diff --git a/test/tests.js b/test/tests.js index 71cb6c78..55c7a1b8 100644 --- a/test/tests.js +++ b/test/tests.js @@ -2420,6 +2420,14 @@ describe('L.patterns', () => { }) describe('L.mapping', () => { + testEq( + () => + L.get( + L.array(L.mapping((x, xs) => [L.andP(I.isArray, [x, ...xs]), [x, xs]])), + [[1, 2, 3], 'abc'] + ), + [[1, [2, 3]]] + ) testEq( () => L.get(L.array(L.mapping(x => [L.andP(L.notP([]), x), x])), [{}, 1, []]), @@ -3127,6 +3135,7 @@ if (process.env.NODE_ENV !== 'production') { testThrows(() => L.mapping(x => [...x, []])) testThrows(() => L.mapping(() => [new XYZ(), []])) testThrows(() => L.mapping((x, y) => [{...x, ...y}, []])) + testThrows(() => L.mapping([(x, y) => x === y, 0])) }) } From be7d7e449bb367552c335ef2b33df8e88f4664d5 Mon Sep 17 00:00:00 2001 From: Vesa Karvonen Date: Wed, 7 Nov 2018 23:11:04 +0200 Subject: [PATCH 9/9] Improve coverage --- test/tests.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/tests.js b/test/tests.js index 55c7a1b8..74ba1d78 100644 --- a/test/tests.js +++ b/test/tests.js @@ -2420,6 +2420,10 @@ describe('L.patterns', () => { }) describe('L.mapping', () => { + testEq( + () => L.get(L.mapping((x, y) => [x, L.orP([y], [...L._, y], {y})]), 'any'), + undefined + ) testEq( () => L.get(