feat: Return type annotations #53

Merged
glen merged 15 commits from return_types into main 2022-08-30 19:36:44 +00:00
9 changed files with 75 additions and 70 deletions
Showing only changes of commit 1ee6da4294 - Show all commits

View File

@ -3,6 +3,7 @@ export * from './Types/Complex.mjs'
export const equalTT = { export const equalTT = {
'Complex<T>,Complex<T>': ({ 'Complex<T>,Complex<T>': ({
T,
'self(T,T)': me 'self(T,T)': me
}) => Returns('boolean', (w, z) => me(w.re, z.re) && me(w.im, z.im)), }) => 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 // NOTE: Although I do not understand exactly why, with typed-function@3.0's

View File

@ -800,26 +800,9 @@ export default class PocomathInstance {
/* First, add the known instantiations, gathering all types needed */ /* First, add the known instantiations, gathering all types needed */
if (ubType) behavior.needsInstantiations.add(ubType) if (ubType) behavior.needsInstantiations.add(ubType)
let instantiationSet = new Set() 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) { for (const othersig in imps) {
const thisUB = upperBounds.exec(othersig) const thisUB = upperBounds.exec(othersig)
if (thisUB) ubTypes.add(thisUB[2]) 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) { for (const othersig in imps) {
let basesig = othersig.replaceAll(templateCall, '') let basesig = othersig.replaceAll(templateCall, '')
const testsig = substituteInSignature( const testsig = substituteInSignature(
@ -859,28 +863,14 @@ export default class PocomathInstance {
for (const possibility of otherTypeCollection) { for (const possibility of otherTypeCollection) {
for (const convtype of this._priorTypes[possibility]) { for (const convtype of this._priorTypes[possibility]) {
if (this.isSubtypeOf(convtype, possibility)) continue if (this.isSubtypeOf(convtype, possibility)) continue
if (ubTypes.has(convtype)) continue if (!(this._atOrBelowSomeType(convtype, ubTypes))) {
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 (belowUB) continue
instantiationSet.add(convtype) instantiationSet.add(convtype)
} }
} }
} }
} }
} }
}
for (const instType of instantiationSet) { for (const instType of instantiationSet) {
if (!(instType in this.Types)) continue if (!(instType in this.Types)) continue
@ -943,22 +933,7 @@ export default class PocomathInstance {
throw new SyntaxError( throw new SyntaxError(
`Cannot find template parameter in ${rawSignature}`) `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 */ /* Now build the catchall implementation */
const self = this const self = this
/* For return type annotation, we may have to fix this to /* For return type annotation, we may have to fix this to
@ -1122,6 +1097,28 @@ export default class PocomathInstance {
return tf 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. /* Takes an arbitrary type and performs an instantiation if necessary.
* @param {string} type The type to instantiate * @param {string} type The type to instantiate
* @param {string | bool | undefined } * @param {string | bool | undefined }

View File

@ -1,3 +1,5 @@
import Returns from '../core/Returns.mjs'
/* Lifted from mathjs/src/utils/number.js */ /* Lifted from mathjs/src/utils/number.js */
/** /**
* Minimum number added to one that makes the result different than one * Minimum number added to one that makes the result different than one
@ -48,5 +50,6 @@ function nearlyEqual (x, y, epsilon) {
export const compare = { export const compare = {
'number,number': ({ 'number,number': ({
config 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))
} }

View File

@ -1,3 +1,5 @@
import Returns from '../core/Returns.mjs'
export * from './Types/number.mjs' export * from './Types/number.mjs'
export const invert = {number: () => n => 1/n} export const invert = {number: () => Returns('number', n => 1/n)}

View File

@ -1,6 +1,6 @@
import Returns from '../core/Returns.mjs'
export * from './Types/number.mjs' export * from './Types/number.mjs'
export const isZero = { export const isZero = {
number: () => n => n === 0, 'T:number': () => Returns('boolean', n => n === 0)
NumInt: () => n => n === 0 // necessary because of generic template
} }

View File

@ -1,3 +1,5 @@
import Returns from '../core/Returns.mjs'
export * from './Types/number.mjs' export * from './Types/number.mjs'
export const one = {number: () => () => 1} export const one = {number: () => Returns('NumInt', () => 1)}

View File

@ -1,15 +1,10 @@
import Returns from '../core/Returns.mjs'
export * from './Types/number.mjs' export * from './Types/number.mjs'
const intquotient = () => (n,d) => { export const quotient = {
'T:number,T': () => Returns('NumInt', (n,d) => {
if (d === 0) return d if (d === 0) return d
return Math.floor(n/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
} }

View File

@ -1,8 +1,10 @@
import Returns from '../core/Returns.mjs'
export * from './Types/number.mjs' export * from './Types/number.mjs'
export const roundquotient = { export const roundquotient = {
'number,number': () => (n,d) => { 'number,number': () => Returns('NumInt', (n,d) => {
if (d === 0) return d if (d === 0) return d
return Math.round(n/d) return Math.round(n/d)
} })
} }

View File

@ -60,6 +60,9 @@ describe('The default full pocomath instance "math"', () => {
assert.strictEqual(quatType, 'Complex<Complex<NumInt>>') assert.strictEqual(quatType, 'Complex<Complex<NumInt>>')
assert.strictEqual( assert.strictEqual(
math.returnTypeOf('multiply', quatType + ',' + quatType), quatType) 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', () => { it('can subtract numbers', () => {