From 00a7f79552694b1b2ba250fa2cd668c0c00095af Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Fri, 25 Mar 2022 11:31:06 -0700 Subject: [PATCH] feat(poortf): add concept of 'author' to an imp of a TF And make it so that if the same author adds imps again, they replace the previous imps from that author. Use this feature to avoid the explicit check for double-inclusion in subtract, and test it worked. --- generic/subtract.js | 8 ++++---- picomathInstance.js | 6 +++--- poortf.js | 29 +++++++++++++++++++++++------ test/_picomath.js | 4 ++++ 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/generic/subtract.js b/generic/subtract.js index 236885c..c64c15b 100644 --- a/generic/subtract.js +++ b/generic/subtract.js @@ -1,9 +1,9 @@ export default function create(pmath) { const add = pmath('add') const negate = pmath('negate') - if (!pmath.subtract) { // avoid double definition at cost of extensibility - pmath('subtract', [args => args.length === 2, - (x, y) => add(x, negate(y))]) - } + return pmath( + 'subtract', + [args => args.length === 2, (x, y) => add(x, negate(y))], + create) return pmath.subtract } diff --git a/picomathInstance.js b/picomathInstance.js index 04afd13..0eed730 100644 --- a/picomathInstance.js +++ b/picomathInstance.js @@ -6,11 +6,11 @@ export default function picomathInstance (instName) { * as a function, it takes a name and 0 or more implementations add adds * them to its poortf property named name, returning that property value. */ - function fn (name, imps) { + function fn (name, imps, author) { if (name in fn) { - fn[name].addImps(imps) + fn[name].addImps(imps, author) } else { - fn[name] = poortf(name, imps) + fn[name] = poortf(name, imps, author) } return fn[name] } diff --git a/poortf.js b/poortf.js index 0b212d7..afbb9dc 100644 --- a/poortf.js +++ b/poortf.js @@ -1,13 +1,29 @@ /* Totally minimal "typed functions" */ -const addImps = (dest, imps) => { +/* helper: dest is really a TF and imps is either nothing, an imp, or + * an array of implementations. author is the entity responsible for these + * implementations. + */ +const addImps = (dest, imps, author) => { if (imps) { if (!Array.isArray(imps[0])) imps = [imps] - for (const imp of imps) dest.push(imp) + if (author) { + if (dest.authors.has(author)) { + const [count, index] = dest.authors.get(author) + if (count) dest.imps.splice(index, count) + } + dest.authors.set(author, [imps.length, dest.imps.length]) + } + for (const imp of imps) dest.imps.push(imp) } } - -export default function poortf (name, imps) { +/* Create a TF, optionally adding some initial imps and further optionally + * registering them to an author. There are two advantages to author + * registration: (1) if the same author adds implementations, the prior imps + * will be deleted, and (2) the author can be invalidated, and the TF will + * lazily call the author back the next time it is called to re-add the imps. + */ +export default function poortf (name, imps, author) { /* This is the (function) object we will return */ function fn () { for (const imp of fn.imps) { @@ -20,8 +36,9 @@ export default function poortf (name, imps) { /* Now dress it up for use */ Object.defineProperty(fn, 'name', {value: name}) fn.imps = [] - addImps(fn.imps, imps) - fn.addImps = newI => addImps(fn.imps, newI) + fn.authors = new Map() // tracks who made each implementation + addImps(fn, imps, author) + fn.addImps = (newI, author) => addImps(fn, newI, author) return fn } diff --git a/test/_picomath.js b/test/_picomath.js index 84ff19b..d0126e7 100644 --- a/test/_picomath.js +++ b/test/_picomath.js @@ -20,4 +20,8 @@ describe('The default full picomath instance "math"', () => { math.complex(11, -4)) assert.deepStrictEqual(math.negate(math.complex(3, '8')).im, -8) }) + + it('does not double-define subtract', () => { + assert.deepStrictEqual(math.subtract.imps.length, 1) + }) })