From 4fdafc751eeee6ecd9872b90ee333ea720b5d91a Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Sat, 23 Jul 2022 02:41:59 +0000 Subject: [PATCH] feat: Add a couple of ways to install generics safely. (#18) Resolves #10. Co-authored-by: Glen Whitney Reviewed-on: https://code.studioinfinity.org/glen/pocomath/pulls/18 --- src/core/PocomathInstance.mjs | 39 ++++++++++++++++++++++++++++++- src/generic/subtract.concrete.mjs | 3 +++ test/custom.mjs | 23 ++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 src/generic/subtract.concrete.mjs diff --git a/src/core/PocomathInstance.mjs b/src/core/PocomathInstance.mjs index dd4a6cc..4d32896 100644 --- a/src/core/PocomathInstance.mjs +++ b/src/core/PocomathInstance.mjs @@ -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) === '_') { diff --git a/src/generic/subtract.concrete.mjs b/src/generic/subtract.concrete.mjs new file mode 100644 index 0000000..39638aa --- /dev/null +++ b/src/generic/subtract.concrete.mjs @@ -0,0 +1,3 @@ +export {subtract} from './subtract.mjs' +export * from '../number/add.mjs' +export * from '../number/negate.mjs' diff --git a/test/custom.mjs b/test/custom.mjs index aabbb8c..008218e 100644 --- a/test/custom.mjs +++ b/test/custom.mjs @@ -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)) + }) + })