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
* exclamation point.
*/
install(ops) {
install = Returns('void', function(ops) {
if (ops instanceof PocomathInstance) {
return _installInstance(ops)
}
@ -197,7 +197,7 @@ export default class PocomathInstance {
}
}
this._installFunctions(stdFunctions)
}
})
/* Merge any number of PocomathInstances or modules: */
static merge(name, ...pieces) {
@ -209,7 +209,7 @@ export default class PocomathInstance {
}
/* 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)) {
this._maybeInstantiate(type)
}
@ -217,13 +217,20 @@ export default class PocomathInstance {
operation = operation.name
}
const details = this._pocoFindSignature(operation, signature)
return returnTypeOf(details.fn, signature, this)
}
if (details) {
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: */
chain(value) {
return makeChain(value, this, this._chainRepository)
}
chain = Returns(
sig => `Chain<${sig}>`,
function(value) {
return makeChain(value, this, this._chainRepository)
}
)
_installInstance(other) {
for (const [type, spec] of Object.entries(other.Types)) {
@ -272,9 +279,9 @@ export default class PocomathInstance {
for (const name of requiredSet) {
for (const type of typeSet) {
try {
const modName = `../${type}/${name}.mjs`
const mod = await import(modName)
this.install(mod)
const moduleName = `../${type}/${name}.mjs`
const module = await import(moduleName)
this.install(module)
} catch (err) {
if (!(err.message.includes('find'))) {
// 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
* the corresponding changes to the _typed object immediately
*/
installType(type, spec) {
installType = Returns('void', function(type, spec) {
const parts = type.split(/[<,>]/)
if (this._templateParam(parts[0])) {
throw new SyntaxError(
@ -415,7 +422,7 @@ export default class PocomathInstance {
const imp = {}
imp[type] = {uses: new Set(), does: () => Returns('string', () => type)}
this._installFunctions({typeOf: imp})
}
})
_addSubtypeTo(sup, sub) {
if (this.isSubtypeOf(sub, sup)) return
@ -428,47 +435,48 @@ export default class PocomathInstance {
}
/* 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)
}
})
/* 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
return this._priorTypes[typeB].has(typeA)
}
})
/* Returns a list of the subtypes of a given type, in topological sorted
* order (i.e, no type on the list contains one that comes after it).
/* Returns a list of the strict ubtypes of a given type, in topological
* 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?
}
})
/* Returns a list of the supertypes of a given type, starting with itself,
* in topological order
*/
supertypesOf(type) {
supertypesOf = Returns('Array<string>', function(type) {
const supList = []
while (type) {
supList.push(type)
type = this.Types[type].refines
}
return supList
}
})
/* Returns the most refined type containing all the types in the array,
* with '' standing for the empty type for convenience. If the second
* argument `convert` is true, a convertible type is considered a
* a subtype (defaults to false).
*/
joinTypes(types, convert) {
joinTypes = Returns('string', function(types, convert) {
let join = ''
for (const type of types) {
join = this._joinTypes(join, type, convert)
}
return join
}
})
/* helper for above */
_joinTypes(typeA, typeB, convert) {
if (!typeA) return typeB
@ -500,9 +508,9 @@ export default class PocomathInstance {
/* Returns a list of all types that have been mentioned in the
* 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))
}
})
/* Used internally to install a template type */
_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,
* 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
if (depth > this._maxDepthSeen ) {
// don't bother with types much deeper thant we have seen
@ -1204,7 +1212,7 @@ export default class PocomathInstance {
}
this.installType(wantsType, newTypeSpec)
return wantsType
}
})
_findSubtypeImpl(name, imps, neededSig) {
if (neededSig in imps) return neededSig
@ -1305,7 +1313,7 @@ export default class PocomathInstance {
}
return result
}
_pocoresolve(name, sig, typedFunction) {
if (!this._typed.isTypedFunction(typedFunction)) {
typedFunction = this[name]

View File

@ -13,8 +13,8 @@ export const floor = {
* be separately activated
*/
bigint: () => Returns('bigint', x => x),
NumInt: () => Returns('NumInt', x => x),
'Complex<bigint>': () => Returns('Complex<bigint>', x => x),
NumInt: () => Returns('NumInt', x => x),
'Complex<bigint>': () => Returns('Complex<bigint>', x => x),
number: ({'equalTT(number,number)': eq}) => Returns('NumInt', n => {
if (eq(n, Math.round(n))) return Math.round(n)

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))
assert.strictEqual(
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', () => {