From 1ee6da429498c02910d623871916be8912f5a606 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Mon, 29 Aug 2022 21:18:59 -0400 Subject: [PATCH] feat(number): Provide return types for all operations --- src/complex/equalTT.mjs | 1 + src/core/PocomathInstance.mjs | 101 +++++++++++++++++----------------- src/number/compare.mjs | 5 +- src/number/invert.mjs | 4 +- src/number/isZero.mjs | 4 +- src/number/one.mjs | 4 +- src/number/quotient.mjs | 17 ++---- src/number/roundquotient.mjs | 6 +- test/_pocomath.mjs | 3 + 9 files changed, 75 insertions(+), 70 deletions(-) diff --git a/src/complex/equalTT.mjs b/src/complex/equalTT.mjs index b979857..6a84c9a 100644 --- a/src/complex/equalTT.mjs +++ b/src/complex/equalTT.mjs @@ -3,6 +3,7 @@ export * from './Types/Complex.mjs' export const equalTT = { 'Complex,Complex': ({ + T, 'self(T,T)': me }) => Returns('boolean', (w, z) => me(w.re, z.re) && me(w.im, z.im)), // NOTE: Although I do not understand exactly why, with typed-function@3.0's diff --git a/src/core/PocomathInstance.mjs b/src/core/PocomathInstance.mjs index f591941..3ac6f94 100644 --- a/src/core/PocomathInstance.mjs +++ b/src/core/PocomathInstance.mjs @@ -800,26 +800,9 @@ export default class PocomathInstance { /* First, add the known instantiations, gathering all types needed */ if (ubType) behavior.needsInstantiations.add(ubType) let instantiationSet = new Set() - for (const instType of behavior.needsInstantiations) { - instantiationSet.add(instType) - const otherTypes = - ubType ? this.subtypesOf(instType) : this._priorTypes[instType] - for (const other of otherTypes) { - instantiationSet.add(other) - } - } - - /* Prevent other existing signatures from blocking use of top-level - * templates via conversions: - */ - let baseSignature = rawSignature.replaceAll(templateCall, '') - /* Any remaining template params are top-level */ - const signature = substituteInSignature( - baseSignature, theTemplateParam, 'any') - const hasTopLevel = (signature !== baseSignature) - if (!ubType && hasTopLevel) { - // collect upper-bound types - const ubTypes = new Set() + const ubTypes = new Set() + if (!ubType) { + // Collect all upper-bound types for this signature for (const othersig in imps) { const thisUB = upperBounds.exec(othersig) if (thisUB) ubTypes.add(thisUB[2]) @@ -839,6 +822,27 @@ export default class PocomathInstance { } } } + } + for (const instType of behavior.needsInstantiations) { + instantiationSet.add(instType) + const otherTypes = + ubType ? this.subtypesOf(instType) : this._priorTypes[instType] + for (const other of otherTypes) { + if (!(this._atOrBelowSomeType(other, ubTypes))) { + instantiationSet.add(other) + } + } + } + + /* Prevent other existing signatures from blocking use of top-level + * templates via conversions: + */ + let baseSignature = rawSignature.replaceAll(templateCall, '') + /* Any remaining template params are top-level */ + const signature = substituteInSignature( + baseSignature, theTemplateParam, 'any') + const hasTopLevel = (signature !== baseSignature) + if (!ubType && hasTopLevel) { for (const othersig in imps) { let basesig = othersig.replaceAll(templateCall, '') const testsig = substituteInSignature( @@ -859,23 +863,9 @@ export default class PocomathInstance { for (const possibility of otherTypeCollection) { for (const convtype of this._priorTypes[possibility]) { if (this.isSubtypeOf(convtype, possibility)) continue - if (ubTypes.has(convtype)) continue - let belowUB = false - for (const anUB of ubTypes) { - if (anUB in this.Templates) { - if (convtype.slice(0, anUB.length) === anUB) { - belowUB = true - break - } - } else { - if (this.isSubtypeOf(convtype, anUB)) { - belowUB = true - break - } - } + if (!(this._atOrBelowSomeType(convtype, ubTypes))) { + instantiationSet.add(convtype) } - if (belowUB) continue - instantiationSet.add(convtype) } } } @@ -943,22 +933,7 @@ export default class PocomathInstance { throw new SyntaxError( `Cannot find template parameter in ${rawSignature}`) } - /* And eliminate template parameters from the dependencies */ - const simplifiedUses = {} - for (const dep of behavior.uses) { - let [func, needsig] = dep.split(/[()]/) - if (needsig) { - const subsig = substituteInSignature( - needsig, theTemplateParam, '') - if (subsig === needsig) { - simplifiedUses[dep] = dep - } else { - simplifiedUses[dep] = func - } - } else { - simplifiedUses[dep] = dep - } - } + /* Now build the catchall implementation */ const self = this /* For return type annotation, we may have to fix this to @@ -1122,6 +1097,28 @@ export default class PocomathInstance { return tf } + /* Takes a type and a set of types and returns true if the type + * is a subtype of some type in the set. + */ + _atOrBelowSomeType(type, typeSet) { + if (typeSet.has(type)) return true + let belowSome = false + for (const anUB of typeSet) { + if (anUB in this.Templates) { + if (type.slice(0, anUB.length) === anUB) { + belowSome = true + break + } + } else { + if (this.isSubtypeOf(type, anUB)) { + belowSome = true + break + } + } + } + return belowSome + } + /* Takes an arbitrary type and performs an instantiation if necessary. * @param {string} type The type to instantiate * @param {string | bool | undefined } diff --git a/src/number/compare.mjs b/src/number/compare.mjs index 4dc865b..c4b1c26 100644 --- a/src/number/compare.mjs +++ b/src/number/compare.mjs @@ -1,3 +1,5 @@ +import Returns from '../core/Returns.mjs' + /* Lifted from mathjs/src/utils/number.js */ /** * Minimum number added to one that makes the result different than one @@ -48,5 +50,6 @@ function nearlyEqual (x, y, epsilon) { export const compare = { 'number,number': ({ config - }) => (x,y) => nearlyEqual(x, y, config.epsilon) ? 0 : (x > y ? 1 : -1) + }) => Returns( + 'NumInt', (x,y) => nearlyEqual(x, y, config.epsilon) ? 0 : (x > y ? 1 : -1)) } diff --git a/src/number/invert.mjs b/src/number/invert.mjs index 4eabe2f..780ad72 100644 --- a/src/number/invert.mjs +++ b/src/number/invert.mjs @@ -1,3 +1,5 @@ +import Returns from '../core/Returns.mjs' + export * from './Types/number.mjs' -export const invert = {number: () => n => 1/n} +export const invert = {number: () => Returns('number', n => 1/n)} diff --git a/src/number/isZero.mjs b/src/number/isZero.mjs index c15549e..0209daa 100644 --- a/src/number/isZero.mjs +++ b/src/number/isZero.mjs @@ -1,6 +1,6 @@ +import Returns from '../core/Returns.mjs' export * from './Types/number.mjs' export const isZero = { - number: () => n => n === 0, - NumInt: () => n => n === 0 // necessary because of generic template + 'T:number': () => Returns('boolean', n => n === 0) } diff --git a/src/number/one.mjs b/src/number/one.mjs index 5726468..e38d0dc 100644 --- a/src/number/one.mjs +++ b/src/number/one.mjs @@ -1,3 +1,5 @@ +import Returns from '../core/Returns.mjs' + export * from './Types/number.mjs' -export const one = {number: () => () => 1} +export const one = {number: () => Returns('NumInt', () => 1)} diff --git a/src/number/quotient.mjs b/src/number/quotient.mjs index e8ed83a..b307709 100644 --- a/src/number/quotient.mjs +++ b/src/number/quotient.mjs @@ -1,15 +1,10 @@ +import Returns from '../core/Returns.mjs' + export * from './Types/number.mjs' -const intquotient = () => (n,d) => { - if (d === 0) return d - return Math.floor(n/d) -} - export const quotient = { - // Hmmm, seem to need all of these because of the generic template version - // Should be a way around that - 'NumInt,NumInt': intquotient, - 'NumInt,number': intquotient, - 'number,NumInt': intquotient, - 'number,number': intquotient + 'T:number,T': () => Returns('NumInt', (n,d) => { + if (d === 0) return d + return Math.floor(n/d) + }) } diff --git a/src/number/roundquotient.mjs b/src/number/roundquotient.mjs index 401d499..8c4c519 100644 --- a/src/number/roundquotient.mjs +++ b/src/number/roundquotient.mjs @@ -1,8 +1,10 @@ +import Returns from '../core/Returns.mjs' + export * from './Types/number.mjs' export const roundquotient = { - 'number,number': () => (n,d) => { + 'number,number': () => Returns('NumInt', (n,d) => { if (d === 0) return d return Math.round(n/d) - } + }) } diff --git a/test/_pocomath.mjs b/test/_pocomath.mjs index 3eff3d3..5d80e85 100644 --- a/test/_pocomath.mjs +++ b/test/_pocomath.mjs @@ -60,6 +60,9 @@ describe('The default full pocomath instance "math"', () => { assert.strictEqual(quatType, 'Complex>') assert.strictEqual( math.returnTypeOf('multiply', quatType + ',' + quatType), quatType) + assert.strictEqual(math.returnTypeOf('isZero', 'NumInt'), 'boolean') + assert.strictEqual( + math.returnTypeOf('roundquotient', 'NumInt,number'), 'NumInt') }) it('can subtract numbers', () => {