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.
This commit is contained in:
Glen Whitney 2022-03-25 11:31:06 -07:00
parent 2c1b6c3364
commit 00a7f79552
4 changed files with 34 additions and 13 deletions

View File

@ -1,9 +1,9 @@
export default function create(pmath) { export default function create(pmath) {
const add = pmath('add') const add = pmath('add')
const negate = pmath('negate') const negate = pmath('negate')
if (!pmath.subtract) { // avoid double definition at cost of extensibility return pmath(
pmath('subtract', [args => args.length === 2, 'subtract',
(x, y) => add(x, negate(y))]) [args => args.length === 2, (x, y) => add(x, negate(y))],
} create)
return pmath.subtract return pmath.subtract
} }

View File

@ -6,11 +6,11 @@ export default function picomathInstance (instName) {
* as a function, it takes a name and 0 or more implementations add adds * 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. * them to its poortf property named name, returning that property value.
*/ */
function fn (name, imps) { function fn (name, imps, author) {
if (name in fn) { if (name in fn) {
fn[name].addImps(imps) fn[name].addImps(imps, author)
} else { } else {
fn[name] = poortf(name, imps) fn[name] = poortf(name, imps, author)
} }
return fn[name] return fn[name]
} }

View File

@ -1,13 +1,29 @@
/* Totally minimal "typed functions" */ /* 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 (imps) {
if (!Array.isArray(imps[0])) imps = [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)
} }
} }
/* Create a TF, optionally adding some initial imps and further optionally
export default function poortf (name, imps) { * 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 */ /* This is the (function) object we will return */
function fn () { function fn () {
for (const imp of fn.imps) { for (const imp of fn.imps) {
@ -20,8 +36,9 @@ export default function poortf (name, imps) {
/* Now dress it up for use */ /* Now dress it up for use */
Object.defineProperty(fn, 'name', {value: name}) Object.defineProperty(fn, 'name', {value: name})
fn.imps = [] fn.imps = []
addImps(fn.imps, imps) fn.authors = new Map() // tracks who made each implementation
fn.addImps = newI => addImps(fn.imps, newI) addImps(fn, imps, author)
fn.addImps = (newI, author) => addImps(fn, newI, author)
return fn return fn
} }

View File

@ -20,4 +20,8 @@ describe('The default full picomath instance "math"', () => {
math.complex(11, -4)) math.complex(11, -4))
assert.deepStrictEqual(math.negate(math.complex(3, '8')).im, -8) assert.deepStrictEqual(math.negate(math.complex(3, '8')).im, -8)
}) })
it('does not double-define subtract', () => {
assert.deepStrictEqual(math.subtract.imps.length, 1)
})
}) })