From fd3d6b2eb3c58c6b3b22f948107cc8c796602003 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Sun, 31 Jul 2022 23:33:58 -0700 Subject: [PATCH] feat: Template implementations --- src/bigint/add.mjs | 4 +-- src/complex/add.mjs | 14 +++++----- src/core/PocomathInstance.mjs | 43 +++++++++++++++++++++++++------ src/generic/arithmetic.mjs | 5 +++- src/generic/reducingOperation.mjs | 12 +++++++++ src/generic/subtract.mjs | 2 +- src/number/add.mjs | 4 +-- test/_pocomath.mjs | 4 ++- test/custom.mjs | 2 ++ 9 files changed, 67 insertions(+), 23 deletions(-) create mode 100644 src/generic/reducingOperation.mjs diff --git a/src/bigint/add.mjs b/src/bigint/add.mjs index 31e2926..1cd296d 100644 --- a/src/bigint/add.mjs +++ b/src/bigint/add.mjs @@ -1,5 +1,3 @@ export * from './Types/bigint.mjs' -export const add = { - '...bigint': () => addends => addends.reduce((x,y) => x+y, 0n) -} +export const add = {'bigint,bigint': () => (a,b) => a+b} diff --git a/src/complex/add.mjs b/src/complex/add.mjs index 58d9eea..8f26167 100644 --- a/src/complex/add.mjs +++ b/src/complex/add.mjs @@ -1,10 +1,12 @@ export * from './Types/Complex.mjs' export const add = { - '...Complex': ({self}) => addends => { - if (addends.length === 0) return {re:0, im:0} - const seed = addends.shift() - return addends.reduce( - (w,z) => ({re: self(w.re, z.re), im: self(w.im, z.im)}), seed) - } + 'Complex,number': ({ + 'self(number,number)': addNum, + 'complex(any,any)': cplx + }) => (z,x) => cplx(addNum(z.re, x), z.im), + 'Complex,Complex': ({ + self, + 'complex(any,any)': cplx + }) => (w,z) => cplx(self(w.re, z.re), self(w.im, z.im)) } diff --git a/src/core/PocomathInstance.mjs b/src/core/PocomathInstance.mjs index 72bfc20..6ef3288 100644 --- a/src/core/PocomathInstance.mjs +++ b/src/core/PocomathInstance.mjs @@ -443,7 +443,26 @@ export default class PocomathInstance { for (const instType of behavior.instantiations) { const signature = substituteInSig(rawSignature, theTemplateParam, instType) - this._addTFimplementation(tf_imps, signature, behavior, instType) + const uses = new Set() + for (const dep of behavior.uses) { + if (this._templateParam(dep)) continue + uses.add(substituteInSig(dep, theTemplateParam, instType)) + } + const patch = (refs) => { + const innerRefs = {} + for (const dep of behavior.uses) { + if (this._templateParam(dep)) { + innerRefs[dep] = instType + } else { + const outerName = substituteInSig( + dep, theTemplateParam, instType) + innerRefs[dep] = refs[outerName] + } + } + const original = behavior.does(innerRefs) + return behavior.does(innerRefs) + } + this._addTFimplementation(tf_imps, signature, {uses, does: patch}) } /* Now add the catchall signature */ const signature = substituteInSig(rawSignature, theTemplateParam, 'any') @@ -464,13 +483,21 @@ export default class PocomathInstance { `Cannot find template parameter in ${rawSignature}`) } const self = this - const patch = (refs) => { - const original = behavior.does(refs) - return (...args) => { - const example = args[exemplar] - console.log('Have to match template to', example) - return original(...args) + const patch = (refs) => (...args) => { + const example = args[exemplar] + const instantiateFor = self.typeOf(example) + refs[theTemplateParam] = instantiateFor + const instCount = behavior.instantiations.size + for (const earlier of self._priorTypes[instantiateFor]) { + behavior.instantiations.add(earlier) } + behavior.instantiations.add(instantiateFor) + if (behavior.instantiations.size > instCount) { + self._invalidate(name) + } + // And for now, we have to rely on the "any" implementation. Hope + // it matches the instantiated one! + return behavior.does(refs)(...args) } this._addTFimplementation( tf_imps, signature, {uses: behavior.uses, does: patch}) @@ -484,7 +511,7 @@ export default class PocomathInstance { * to typed-function implementations and inserts the result into plain object * imps */ - _addTFimplementation(imps, signature, behavior, instType) { + _addTFimplementation(imps, signature, behavior) { const {uses, does} = behavior if (uses.length === 0) { imps[signature] = does() diff --git a/src/generic/arithmetic.mjs b/src/generic/arithmetic.mjs index 8bcc904..7576dbe 100644 --- a/src/generic/arithmetic.mjs +++ b/src/generic/arithmetic.mjs @@ -1,8 +1,11 @@ +import {reducingOperation} from './reducingOperation.mjs' + export * from './Types/generic.mjs' +export const add = reducingOperation export {lcm} from './lcm.mjs' export {mod} from './mod.mjs' -export {multiply} from './multiply.mjs' +export const multiply = reducingOperation export {divide} from './divide.mjs' export {sign} from './sign.mjs' export {sqrt} from './sqrt.mjs' diff --git a/src/generic/reducingOperation.mjs b/src/generic/reducingOperation.mjs new file mode 100644 index 0000000..101a8ec --- /dev/null +++ b/src/generic/reducingOperation.mjs @@ -0,0 +1,12 @@ +export * from './Types/generic.mjs' + +export const reducingOperation = { + 'undefined': () => u => u, + 'undefined,...any': () => (u, rest) => u, + 'any,undefined': () => (x, u) => u, + any: () => x => x, + 'any,any,...any': ({ + self + }) => (a,b,rest) => [b, ...rest].reduce((x,y) => self(x,y), a) +} + diff --git a/src/generic/subtract.mjs b/src/generic/subtract.mjs index 1c93b39..b048d0c 100644 --- a/src/generic/subtract.mjs +++ b/src/generic/subtract.mjs @@ -1,3 +1,3 @@ export const subtract = { - 'T,T': ({'add(T)': addT, 'negate(T)': negT}) => (x,y) => addT(x, negT(y)) + 'T,T': ({'add(T,T)': addT, 'negate(T)': negT}) => (x,y) => addT(x, negT(y)) } diff --git a/src/number/add.mjs b/src/number/add.mjs index e6610ee..7d79637 100644 --- a/src/number/add.mjs +++ b/src/number/add.mjs @@ -1,5 +1,3 @@ export * from './Types/number.mjs' -export const add = { - '...number': () => addends => addends.reduce((x,y) => x+y, 0), -} +export const add = {'number,number': () => (m,n) => m+n} diff --git a/test/_pocomath.mjs b/test/_pocomath.mjs index 3e9f0a0..b17ec5c 100644 --- a/test/_pocomath.mjs +++ b/test/_pocomath.mjs @@ -21,12 +21,14 @@ describe('The default full pocomath instance "math"', () => { it('can subtract numbers', () => { assert.strictEqual(math.subtract(12, 5), 7) + //assert.strictEqual(math.subtract(3n, 1.5), 1.5) }) it('can add numbers', () => { assert.strictEqual(math.add(3, 4), 7) assert.strictEqual(math.add(1.5, 2.5, 3.5), 7.5) assert.strictEqual(math.add(Infinity), Infinity) + assert.throws(() => math.add(3n, -1.5), TypeError) }) it('can negate numbers', () => { @@ -55,10 +57,10 @@ describe('The default full pocomath instance "math"', () => { assert.deepStrictEqual( math.subtract(math.complex(1,1), math.complex(2,-2)), math.complex(-1,3)) + assert.strictEqual(math.negate(math.complex(3, 8)).im, -8) assert.deepStrictEqual( math.subtract(16, math.add(3, math.complex(0,4), 2)), math.complex(11, -4)) - assert.strictEqual(math.negate(math.complex(3, 8)).im, -8) }) it('handles bigints', () => { diff --git a/test/custom.mjs b/test/custom.mjs index ab26563..7aef959 100644 --- a/test/custom.mjs +++ b/test/custom.mjs @@ -3,6 +3,7 @@ import math from '../src/pocomath.mjs' import PocomathInstance from '../src/core/PocomathInstance.mjs' import * as numbers from '../src/number/all.mjs' import * as numberAdd from '../src/number/add.mjs' +import {add as genericAdd} from '../src/generic/arithmetic.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' @@ -66,6 +67,7 @@ describe('A custom instance', () => { const cherry = new PocomathInstance('cherry') cherry.install(numberAdd) await extendToComplex(cherry) + cherry.install({add: genericAdd}) /* Now we have an instance that supports addition for number and complex and little else: */