feat: Add a couple of ways to install generics safely. (#18)

Resolves #10.

Co-authored-by: Glen Whitney <glen@studioinfinity.org>
Reviewed-on: #18
This commit is contained in:
Glen Whitney 2022-07-23 02:41:59 +00:00
parent 2a60cc0989
commit 4fdafc751e
3 changed files with 64 additions and 1 deletions

View File

@ -6,7 +6,7 @@ export default class PocomathInstance {
* in that if a new top-level PocomathInstance method is added, its name
* must be added to this list.
*/
static reserved = new Set(['install'])
static reserved = new Set(['install', 'importDependencies'])
constructor(name) {
this.name = name
@ -52,6 +52,43 @@ export default class PocomathInstance {
for (const key in ops) this._installOp(key, ops[key])
}
/**
* Import (and install) all dependencies of previously installed functions,
* for the specified types.
*
* @param {string[]} types A list of type names
*/
async importDependencies(types) {
const doneSet = new Set(['self']) // nothing to do for self dependencies
while (true) {
const requiredSet = new Set()
/* Grab all of the known deps */
for (const func in this._imps) {
if (func === 'Types') continue
for (const definition of Object.values(this._imps[func])) {
for (const dependency of definition[0]) {
const depName = dependency.split('(',1)[0]
if (doneSet.has(depName)) continue
requiredSet.add(depName)
}
}
}
if (requiredSet.size === 0) break
for (const name of requiredSet) {
for (const type of types) {
try {
const modName = `../${type}/${name}.mjs`
const mod = await import(modName)
this.install(mod)
} catch (err) {
// No such module, but that's OK
}
}
doneSet.add(name)
}
}
}
/* Used internally by install, see the documentation there */
_installOp(name, implementations) {
if (name.charAt(0) === '_') {

View File

@ -0,0 +1,3 @@
export {subtract} from './subtract.mjs'
export * from '../number/add.mjs'
export * from '../number/negate.mjs'

View File

@ -6,6 +6,9 @@ import * as numberAdd from '../src/number/add.mjs'
import * as complex from '../src/complex/all.mjs'
import * as complexAdd from '../src/complex/add.mjs'
import * as complexNegate from '../src/complex/negate.mjs'
import * as complexComplex from '../src/complex/complex.mjs'
import * as concreteSubtract from '../src/generic/subtract.concrete.mjs'
import * as genericSubtract from '../src/generic/subtract.mjs'
import extendToComplex from '../src/complex/extendToComplex.mjs'
const bw = new PocomathInstance('backwards')
@ -55,4 +58,24 @@ describe('A custom instance', () => {
assert.strictEqual('subtract' in cherry, false)
assert.strictEqual('negate' in cherry, false)
})
it("can use bundles that are closed under dependency", () => {
const ok = new PocomathInstance('concrete')
ok.install(concreteSubtract)
assert.strictEqual(ok.subtract(7, 5), 2)
})
it("can load generics and then import their dependences", async function () {
const chase = new PocomathInstance('Chase Dependencies')
chase.install(genericSubtract)
chase.install(complexComplex) // for convenience to build complex numbers
await chase.importDependencies(['bigint', 'complex'])
/* Now we have an instance that supports subtraction for Gaussian
integers.
*/
assert.deepStrictEqual(
chase.subtract(chase.complex(3n, 2n), chase.complex(2n, 5n)),
math.complex(1n, -3n))
})
})