fix: prevent obsolete typed-functions from hanging around

In other words, if name a depends on b and b is invalidated (because
  of added implementations), then a must be invalidated as well.

  Also adds a specific test (custom/piecemeal) that tests this.
This commit is contained in:
Glen Whitney 2022-07-19 11:48:52 -07:00
parent 66cbccfbbe
commit 4999cad775
3 changed files with 35 additions and 2 deletions

View File

@ -5,6 +5,7 @@ export default class PocomathInstance {
constructor(name) { constructor(name) {
this.name = name this.name = name
this._imps = {} this._imps = {}
this._affects = {}
} }
/** /**
@ -46,6 +47,14 @@ export default class PocomathInstance {
`Conflicting definitions of ${signature} for ${name}`) `Conflicting definitions of ${signature} for ${name}`)
} else { } else {
opImps[signature] = implementations[signature] opImps[signature] = implementations[signature]
for (const dep of implementations[signature][0]) {
const depname = dep.split('(', 1)[0]
if (depname === 'self') continue
if (!(depname in this._affects)) {
this._affects[depname] = new Set()
}
this._affects[depname].add(name)
}
} }
} }
} }
@ -62,6 +71,11 @@ export default class PocomathInstance {
if (!(name in this._imps)) { if (!(name in this._imps)) {
this._imps[name] = {} this._imps[name] = {}
} }
if (name in this._affects) {
for (const ancestor of this._affects[name]) {
this._invalidate(ancestor)
}
}
} }
/** /**

View File

@ -1,4 +1,8 @@
import './Complex.mjs' import {numComplex} from './Complex.mjs'
export const negate = { export const negate = {
Complex: [['self'], ref => z => ({re: ref.self(z.re), im: ref.self(z.im)})] /* need a "base case" to avoid infinite self-reference */
Complex: [['self'], ref => z => {
if (numComplex(z)) return {re: -z.re, im: -z.im}
return {re: ref.self(z.re), im: ref.self(z.im)}
}]
} }

View File

@ -4,12 +4,16 @@ import typed from 'typed-function'
import PocomathInstance from '../PocomathInstance.mjs' import PocomathInstance from '../PocomathInstance.mjs'
import * as numbers from '../number/all.mjs' import * as numbers from '../number/all.mjs'
import * as complex from '../complex/all.mjs' import * as complex from '../complex/all.mjs'
import * as complexAdd from '../complex/add.mjs'
import * as complexNegate from '../complex/negate.mjs'
const bw = new PocomathInstance('backwards') const bw = new PocomathInstance('backwards')
describe('A custom instance', () => { describe('A custom instance', () => {
it("works when partially assembled", () => { it("works when partially assembled", () => {
bw.install(complex) bw.install(complex)
assert.deepStrictEqual(bw.add(2, bw.complex(0, 3)), {re: 2, im: 3}) assert.deepStrictEqual(bw.add(2, bw.complex(0, 3)), {re: 2, im: 3})
assert.deepStrictEqual(bw.negate(2), bw.complex(-2,-0))
assert.deepStrictEqual(bw.subtract(2, bw.complex(0, 3)), {re: 2, im: -3})
}) })
it("can be assembled in any order", () => { it("can be assembled in any order", () => {
@ -23,4 +27,15 @@ describe('A custom instance', () => {
math.complex(11, -4)) // note both instances coexist math.complex(11, -4)) // note both instances coexist
assert.deepStrictEqual(bw.negate(math.complex(3, '8')).im, -8) assert.deepStrictEqual(bw.negate(math.complex(3, '8')).im, -8)
}) })
it("can be assembled piecemeal", () => {
const pm = new PocomathInstance('piecemeal')
pm.install(numbers)
assert.strictEqual(pm.subtract(5, 10), -5)
pm.install(complexAdd)
pm.install(complexNegate)
// Should be enough to allow complex subtraction, as subtract is generic
assert.deepStrictEqual(
pm.subtract({re:5, im:0}, {re:10, im:1}), {re:-5, im: -1})
})
}) })