feat: Return type annotations #53
@ -3,6 +3,7 @@ export * from './Types/Complex.mjs'
|
||||
|
||||
export const equalTT = {
|
||||
'Complex<T>,Complex<T>': ({
|
||||
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
|
||||
|
@ -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()
|
||||
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,28 +863,14 @@ 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 (belowUB) continue
|
||||
if (!(this._atOrBelowSomeType(convtype, ubTypes))) {
|
||||
instantiationSet.add(convtype)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const instType of instantiationSet) {
|
||||
if (!(instType in this.Types)) continue
|
||||
@ -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 }
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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)}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)}
|
||||
|
@ -1,15 +1,10 @@
|
||||
import Returns from '../core/Returns.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
|
||||
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
|
||||
})
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -60,6 +60,9 @@ describe('The default full pocomath instance "math"', () => {
|
||||
assert.strictEqual(quatType, 'Complex<Complex<NumInt>>')
|
||||
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', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user