feat: Return type annotations #53

Merged
glen merged 15 commits from return_types into main 2022-08-30 19:36:44 +00:00
3 changed files with 44 additions and 32 deletions
Showing only changes of commit 0950d7d585 - Show all commits

View File

@ -164,7 +164,7 @@ export default class PocomathInstance {
* instantiation can be accomplished by prefixin the signature with an * instantiation can be accomplished by prefixin the signature with an
* exclamation point. * exclamation point.
*/ */
install(ops) { install = Returns('void', function(ops) {
if (ops instanceof PocomathInstance) { if (ops instanceof PocomathInstance) {
return _installInstance(ops) return _installInstance(ops)
} }
@ -197,7 +197,7 @@ export default class PocomathInstance {
} }
} }
this._installFunctions(stdFunctions) this._installFunctions(stdFunctions)
} })
/* Merge any number of PocomathInstances or modules: */ /* Merge any number of PocomathInstances or modules: */
static merge(name, ...pieces) { static merge(name, ...pieces) {
@ -209,7 +209,7 @@ export default class PocomathInstance {
} }
/* Determine the return type of an operation given an input signature */ /* Determine the return type of an operation given an input signature */
returnTypeOf(operation, signature) { returnTypeOf = Returns('string', function(operation, signature) {
for (const type of typeListOfSignature(signature)) { for (const type of typeListOfSignature(signature)) {
this._maybeInstantiate(type) this._maybeInstantiate(type)
} }
@ -217,13 +217,20 @@ export default class PocomathInstance {
operation = operation.name operation = operation.name
} }
const details = this._pocoFindSignature(operation, signature) const details = this._pocoFindSignature(operation, signature)
if (details) {
return returnTypeOf(details.fn, signature, this) return returnTypeOf(details.fn, signature, this)
} }
console.log('Checking return type of', operation)
return returnTypeOf(this[operation], signature, this)
})
/* Return a chain object for this instance with a given value: */ /* Return a chain object for this instance with a given value: */
chain(value) { chain = Returns(
sig => `Chain<${sig}>`,
function(value) {
return makeChain(value, this, this._chainRepository) return makeChain(value, this, this._chainRepository)
} }
)
_installInstance(other) { _installInstance(other) {
for (const [type, spec] of Object.entries(other.Types)) { for (const [type, spec] of Object.entries(other.Types)) {
@ -272,9 +279,9 @@ export default class PocomathInstance {
for (const name of requiredSet) { for (const name of requiredSet) {
for (const type of typeSet) { for (const type of typeSet) {
try { try {
const modName = `../${type}/${name}.mjs` const moduleName = `../${type}/${name}.mjs`
const mod = await import(modName) const module = await import(moduleName)
this.install(mod) this.install(module)
} catch (err) { } catch (err) {
if (!(err.message.includes('find'))) { if (!(err.message.includes('find'))) {
// Not just a error because module doesn't exist // Not just a error because module doesn't exist
@ -315,7 +322,7 @@ export default class PocomathInstance {
* Implementation note: unlike _installFunctions below, we can make * Implementation note: unlike _installFunctions below, we can make
* the corresponding changes to the _typed object immediately * the corresponding changes to the _typed object immediately
*/ */
installType(type, spec) { installType = Returns('void', function(type, spec) {
const parts = type.split(/[<,>]/) const parts = type.split(/[<,>]/)
if (this._templateParam(parts[0])) { if (this._templateParam(parts[0])) {
throw new SyntaxError( throw new SyntaxError(
@ -415,7 +422,7 @@ export default class PocomathInstance {
const imp = {} const imp = {}
imp[type] = {uses: new Set(), does: () => Returns('string', () => type)} imp[type] = {uses: new Set(), does: () => Returns('string', () => type)}
this._installFunctions({typeOf: imp}) this._installFunctions({typeOf: imp})
} })
_addSubtypeTo(sup, sub) { _addSubtypeTo(sup, sub) {
if (this.isSubtypeOf(sub, sup)) return if (this.isSubtypeOf(sub, sup)) return
@ -428,47 +435,48 @@ export default class PocomathInstance {
} }
/* Returns true if typeA is a subtype of type B */ /* Returns true if typeA is a subtype of type B */
isSubtypeOf(typeA, typeB) { isSubtypeOf = Returns('boolean', function(typeA, typeB) {
return this._subtypes[typeB].includes(typeA) return this._subtypes[typeB].includes(typeA)
} })
/* Returns true if typeA is a subtype of or converts to type B */ /* Returns true if typeA is a subtype of or converts to type B */
isPriorTo(typeA, typeB) { isPriorTo = Returns('boolean', function(typeA, typeB) {
if (!(typeB in this._priorTypes)) return false if (!(typeB in this._priorTypes)) return false
return this._priorTypes[typeB].has(typeA) return this._priorTypes[typeB].has(typeA)
} })
/* Returns a list of the subtypes of a given type, in topological sorted /* Returns a list of the strict ubtypes of a given type, in topological
* order (i.e, no type on the list contains one that comes after it). * sorted order (i.e, no type on the list contains one that comes after it).
*/ */
subtypesOf(type) { subtypesOf = Returns('Array<string>', function(type) {
return this._subtypes[type] // should we clone? return this._subtypes[type] // should we clone?
} })
/* Returns a list of the supertypes of a given type, starting with itself, /* Returns a list of the supertypes of a given type, starting with itself,
* in topological order * in topological order
*/ */
supertypesOf(type) { supertypesOf = Returns('Array<string>', function(type) {
const supList = [] const supList = []
while (type) { while (type) {
supList.push(type) supList.push(type)
type = this.Types[type].refines type = this.Types[type].refines
} }
return supList return supList
} })
/* Returns the most refined type containing all the types in the array, /* Returns the most refined type containing all the types in the array,
* with '' standing for the empty type for convenience. If the second * with '' standing for the empty type for convenience. If the second
* argument `convert` is true, a convertible type is considered a * argument `convert` is true, a convertible type is considered a
* a subtype (defaults to false). * a subtype (defaults to false).
*/ */
joinTypes(types, convert) { joinTypes = Returns('string', function(types, convert) {
let join = '' let join = ''
for (const type of types) { for (const type of types) {
join = this._joinTypes(join, type, convert) join = this._joinTypes(join, type, convert)
} }
return join return join
} })
/* helper for above */ /* helper for above */
_joinTypes(typeA, typeB, convert) { _joinTypes(typeA, typeB, convert) {
if (!typeA) return typeB if (!typeA) return typeB
@ -500,9 +508,9 @@ export default class PocomathInstance {
/* Returns a list of all types that have been mentioned in the /* Returns a list of all types that have been mentioned in the
* signatures of operations, but which have not actually been installed: * signatures of operations, but which have not actually been installed:
*/ */
undefinedTypes() { undefinedTypes = Returns('Array<string>', function() {
return Array.from(this._seenTypes).filter(t => !(t in this.Types)) return Array.from(this._seenTypes).filter(t => !(t in this.Types))
} })
/* Used internally to install a template type */ /* Used internally to install a template type */
_installTemplateType(type, spec) { _installTemplateType(type, spec) {
@ -1137,7 +1145,7 @@ export default class PocomathInstance {
* Returns the name of the type if added, false if it was already there, * Returns the name of the type if added, false if it was already there,
* and undefined if the type is declined (because of being nested too deep). * and undefined if the type is declined (because of being nested too deep).
*/ */
instantiateTemplate(base, instantiator) { instantiateTemplate = Returns('void', function(base, instantiator) {
const depth = instantiator.split('<').length const depth = instantiator.split('<').length
if (depth > this._maxDepthSeen ) { if (depth > this._maxDepthSeen ) {
// don't bother with types much deeper thant we have seen // don't bother with types much deeper thant we have seen
@ -1204,7 +1212,7 @@ export default class PocomathInstance {
} }
this.installType(wantsType, newTypeSpec) this.installType(wantsType, newTypeSpec)
return wantsType return wantsType
} })
_findSubtypeImpl(name, imps, neededSig) { _findSubtypeImpl(name, imps, neededSig) {
if (neededSig in imps) return neededSig if (neededSig in imps) return neededSig

View File

@ -34,6 +34,10 @@ describe('The default full pocomath instance "math"', () => {
math.add(3, math.complex(2.5, 1)), math.complex(5.5, 1)) math.add(3, math.complex(2.5, 1)), math.complex(5.5, 1))
assert.strictEqual( assert.strictEqual(
math.returnTypeOf('add', 'Complex<number>,NumInt'), 'Complex<number>') math.returnTypeOf('add', 'Complex<number>,NumInt'), 'Complex<number>')
assert.strictEqual(
math.returnTypeOf('chain', 'bigint'), 'Chain<bigint>')
assert.strictEqual(
math.returnTypeOf('returnTypeOf', 'string,string'), 'string')
}) })
it('can subtract numbers', () => { it('can subtract numbers', () => {