|
|
|
@ -9,6 +9,7 @@ const anySpec = {} // fixed dummy specification of 'any' type
|
|
|
|
|
|
|
|
|
|
const theTemplateParam = 'T' // First pass: only allow this one exact parameter
|
|
|
|
|
const restTemplateParam = `...${theTemplateParam}`
|
|
|
|
|
const templateCall = `<${theTemplateParam}>`
|
|
|
|
|
const templateFromParam = 'U' // For defining covariant conversions
|
|
|
|
|
|
|
|
|
|
/* Returns a new signature just like sig but with the parameter replaced by
|
|
|
|
@ -21,6 +22,8 @@ function substituteInSignature(signature, parameter, type) {
|
|
|
|
|
return sig.replaceAll(pattern, type)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let lastWhatToDo = null // used in an infinite descent check
|
|
|
|
|
|
|
|
|
|
export default class PocomathInstance {
|
|
|
|
|
/* Disallowed names for ops; beware, this is slightly non-DRY
|
|
|
|
|
* in that if a new top-level PocomathInstance method is added, its name
|
|
|
|
@ -55,24 +58,39 @@ export default class PocomathInstance {
|
|
|
|
|
this._affects = {}
|
|
|
|
|
this._typed = typed.create()
|
|
|
|
|
this._typed.clear()
|
|
|
|
|
this._typed.addTypes([{name: 'ground', test: () => true}])
|
|
|
|
|
// The following is an additional typed-function universe for resolving
|
|
|
|
|
// uninstantiated template instances. It is linked to the main one in
|
|
|
|
|
// its onMismatch function, below:
|
|
|
|
|
this._metaTyped = typed.create()
|
|
|
|
|
this._metaTyped.clear()
|
|
|
|
|
// And these are the meta bindings: (I think we don't need separate
|
|
|
|
|
// invalidation for them as they are only accessed through a main call.)
|
|
|
|
|
this._meta = {} // The resulting typed-functions
|
|
|
|
|
this._metaTFimps = {} // and their implementations
|
|
|
|
|
const me = this
|
|
|
|
|
const myTyped = this._typed
|
|
|
|
|
this._typed.onMismatch = (name, args, sigs) => {
|
|
|
|
|
if (me._invalid.has(name)) {
|
|
|
|
|
return me[name](...args) // rebuild implementation and try again
|
|
|
|
|
// rebuild implementation and try again
|
|
|
|
|
return me[name](...args)
|
|
|
|
|
}
|
|
|
|
|
myTyped.throwMismatchError(name, args, sigs)
|
|
|
|
|
const metaversion = me._meta[name]
|
|
|
|
|
if (metaversion) {
|
|
|
|
|
return me._meta[name](...args)
|
|
|
|
|
}
|
|
|
|
|
me._typed.throwMismatchError(name, args, sigs)
|
|
|
|
|
}
|
|
|
|
|
/* List of types installed in the instance. We start with just dummies
|
|
|
|
|
* for the 'any' type and for type parameters:
|
|
|
|
|
*/
|
|
|
|
|
// List of types installed in the instance: (We start with just dummies
|
|
|
|
|
// for the 'any' type and for type parameters.)
|
|
|
|
|
this.Types = {any: anySpec}
|
|
|
|
|
this.Types[theTemplateParam] = anySpec
|
|
|
|
|
this.Types.ground = anySpec
|
|
|
|
|
// All the template types that have been defined
|
|
|
|
|
// Types that have been moved into the metaverse:
|
|
|
|
|
this._metafiedTypes = new Set()
|
|
|
|
|
// All the template types that have been defined:
|
|
|
|
|
this.Templates = {}
|
|
|
|
|
// The actual type testing functions
|
|
|
|
|
// And their instantiations:
|
|
|
|
|
this._instantiationsOf = {}
|
|
|
|
|
// The actual type testing functions:
|
|
|
|
|
this._typeTests = {}
|
|
|
|
|
// For each type, gives all of its (in)direct subtypes in topo order:
|
|
|
|
|
this._subtypes = {}
|
|
|
|
@ -87,27 +105,19 @@ export default class PocomathInstance {
|
|
|
|
|
this._maxDepthSeen = 1 // deepest template nesting we've actually encountered
|
|
|
|
|
this._invalid = new Set() // methods that are currently invalid
|
|
|
|
|
this._config = {predictable: false, epsilon: 1e-12}
|
|
|
|
|
const self = this
|
|
|
|
|
this.config = new Proxy(this._config, {
|
|
|
|
|
get: (target, property) => target[property],
|
|
|
|
|
set: (target, property, value) => {
|
|
|
|
|
if (value !== target[property]) {
|
|
|
|
|
target[property] = value
|
|
|
|
|
self._invalidateDependents('config')
|
|
|
|
|
me._invalidateDependents('config')
|
|
|
|
|
}
|
|
|
|
|
return true // successful
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
this._plainFunctions = new Set() // the names of the plain functions
|
|
|
|
|
this._chainRepository = {} // place to store chainified functions
|
|
|
|
|
|
|
|
|
|
this._installFunctions({
|
|
|
|
|
typeOf: {
|
|
|
|
|
ground: {uses: new Set(), does: () => Returns('string', () => 'any')}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
this.joinTypes = this.joinTypes.bind(this)
|
|
|
|
|
this.joinTypes = this.joinTypes.bind(me)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -330,9 +340,10 @@ export default class PocomathInstance {
|
|
|
|
|
`Type name '${type}' reserved for template parameter`)
|
|
|
|
|
}
|
|
|
|
|
if (parts.some(this._templateParam.bind(this))) {
|
|
|
|
|
// It's a template, deal with it separately
|
|
|
|
|
// It's an uninstantiated template, deal with it separately
|
|
|
|
|
return this._installTemplateType(type, spec)
|
|
|
|
|
}
|
|
|
|
|
const base = parts[0]
|
|
|
|
|
if (type in this.Types) {
|
|
|
|
|
if (spec !== this.Types[type]) {
|
|
|
|
|
throw new SyntaxError(`Conflicting definitions of type ${type}`)
|
|
|
|
@ -345,7 +356,7 @@ export default class PocomathInstance {
|
|
|
|
|
}
|
|
|
|
|
let beforeType = spec.refines
|
|
|
|
|
if (!beforeType) {
|
|
|
|
|
beforeType = 'ground'
|
|
|
|
|
beforeType = 'any'
|
|
|
|
|
for (const other of spec.before || []) {
|
|
|
|
|
if (other in this.Types) {
|
|
|
|
|
beforeType = other
|
|
|
|
@ -387,6 +398,21 @@ export default class PocomathInstance {
|
|
|
|
|
for (const subtype of this._subtypes[from]) {
|
|
|
|
|
this._priorTypes[nextSuper].add(subtype)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add the conversion in the metaverse if need be: */
|
|
|
|
|
const toParts = nextSuper.split('<', 2)
|
|
|
|
|
if (toParts.length > 1) {
|
|
|
|
|
const fromParts = from.split('<', 2)
|
|
|
|
|
if (fromParts.length === 1 || fromParts[0] !== toParts[0]) {
|
|
|
|
|
this._metafy(from)
|
|
|
|
|
try {
|
|
|
|
|
this._metaTyped.addConversion(
|
|
|
|
|
{from, to: toParts[0], convert: spec.from[from]})
|
|
|
|
|
} catch {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nextSuper = this.Types[nextSuper].refines
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -411,6 +437,16 @@ export default class PocomathInstance {
|
|
|
|
|
convert: this.Types[to].from[fromtype]
|
|
|
|
|
})
|
|
|
|
|
this._invalidateDependents(':' + nextSuper)
|
|
|
|
|
/* Add the conversion in the metaverse if need be: */
|
|
|
|
|
const toParts = nextSuper.split('<', 2)
|
|
|
|
|
if (toParts.length > 1 && base !== toParts[0]) {
|
|
|
|
|
this._metafy(type)
|
|
|
|
|
this._metaTyped.addConversion({
|
|
|
|
|
from: type,
|
|
|
|
|
to: toParts[0],
|
|
|
|
|
convert: this.Types[to].from[fromtype]
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
}
|
|
|
|
|
this._priorTypes[nextSuper].add(type)
|
|
|
|
@ -425,6 +461,12 @@ export default class PocomathInstance {
|
|
|
|
|
this._installFunctions({typeOf: imp})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
_metafy(type) {
|
|
|
|
|
if (this._metafiedTypes.has(type)) return
|
|
|
|
|
this._metaTyped.addTypes([{name: type, test: this._typeTests[type]}])
|
|
|
|
|
this._metafiedTypes.add(type)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_addSubtypeTo(sup, sub) {
|
|
|
|
|
if (this.isSubtypeOf(sub, sup)) return
|
|
|
|
|
const supSubs = this._subtypes[sup]
|
|
|
|
@ -435,8 +477,10 @@ export default class PocomathInstance {
|
|
|
|
|
supSubs.splice(i, 0, sub)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns true if typeA is a subtype of type B */
|
|
|
|
|
/* Returns true if typeA is a strict subtype of type B */
|
|
|
|
|
isSubtypeOf = Returns('boolean', function(typeA, typeB) {
|
|
|
|
|
// Currently not considering types to be a subtype of 'any'
|
|
|
|
|
if (typeB === 'any' || typeA === 'any') return false
|
|
|
|
|
return this._subtypes[typeB].includes(typeA)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
@ -483,7 +527,6 @@ export default class PocomathInstance {
|
|
|
|
|
if (!typeA) return typeB
|
|
|
|
|
if (!typeB) return typeA
|
|
|
|
|
if (typeA === 'any' || typeB === 'any') return 'any'
|
|
|
|
|
if (typeA === 'ground' || typeB === 'ground') return 'ground'
|
|
|
|
|
if (typeA === typeB) return typeA
|
|
|
|
|
const subber = convert ? this._priorTypes : this._subtypes
|
|
|
|
|
const pick = convert ? 'has' : 'includes'
|
|
|
|
@ -510,7 +553,8 @@ export default class PocomathInstance {
|
|
|
|
|
* signatures of operations, but which have not actually been installed:
|
|
|
|
|
*/
|
|
|
|
|
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 || t in this.Templates))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
/* Used internally to install a template type */
|
|
|
|
@ -526,6 +570,18 @@ export default class PocomathInstance {
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// install the "base type" in the meta universe:
|
|
|
|
|
let beforeType = 'any'
|
|
|
|
|
for (const other of spec.before || []) {
|
|
|
|
|
if (other in this.templates) {
|
|
|
|
|
beforeType = other
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
this._metaTyped.addTypes([{name: base, test: spec.base}], beforeType)
|
|
|
|
|
this._instantiationsOf[base] = new Set()
|
|
|
|
|
|
|
|
|
|
// update the typeOf function
|
|
|
|
|
const imp = {}
|
|
|
|
|
imp[type] = {
|
|
|
|
@ -534,6 +590,9 @@ export default class PocomathInstance {
|
|
|
|
|
}
|
|
|
|
|
this._installFunctions({typeOf: imp})
|
|
|
|
|
|
|
|
|
|
// Invalidate any functions that reference this template type:
|
|
|
|
|
this._invalidateDependents(':' + base)
|
|
|
|
|
|
|
|
|
|
// Nothing else actually happens until we match a template parameter
|
|
|
|
|
this.Templates[base] = {type, spec}
|
|
|
|
|
}
|
|
|
|
@ -578,12 +637,11 @@ export default class PocomathInstance {
|
|
|
|
|
}
|
|
|
|
|
opImps[signature] = {
|
|
|
|
|
explicit,
|
|
|
|
|
resolved: false,
|
|
|
|
|
uses: behavior.uses,
|
|
|
|
|
does: behavior.does
|
|
|
|
|
}
|
|
|
|
|
if (explicit) {
|
|
|
|
|
opImps[signature].resolved = false
|
|
|
|
|
} else {
|
|
|
|
|
if (!explicit) {
|
|
|
|
|
opImps[signature].hasInstantiations = {}
|
|
|
|
|
opImps[signature].needsInstantiations = new Set()
|
|
|
|
|
}
|
|
|
|
@ -628,6 +686,8 @@ export default class PocomathInstance {
|
|
|
|
|
_invalidate(name, reason) {
|
|
|
|
|
if (!(name in this._imps)) {
|
|
|
|
|
this._imps[name] = {}
|
|
|
|
|
this._TFimps[name] = {}
|
|
|
|
|
this._metaTFimps[name] = {}
|
|
|
|
|
}
|
|
|
|
|
if (reason) {
|
|
|
|
|
// Make sure no TF imps that depend on reason remain:
|
|
|
|
@ -692,10 +752,8 @@ export default class PocomathInstance {
|
|
|
|
|
if (!imps) {
|
|
|
|
|
throw new SyntaxError(`No implementations for ${name}`)
|
|
|
|
|
}
|
|
|
|
|
if (!(this._TFimps[name])) {
|
|
|
|
|
this._TFimps[name] = {}
|
|
|
|
|
}
|
|
|
|
|
const tf_imps = this._TFimps[name]
|
|
|
|
|
const meta_imps = this._metaTFimps[name]
|
|
|
|
|
/* Collect the entries we know the types for */
|
|
|
|
|
const usableEntries = []
|
|
|
|
|
for (const entry of Object.entries(imps)) {
|
|
|
|
@ -751,6 +809,79 @@ export default class PocomathInstance {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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()
|
|
|
|
|
for (const othersig in imps) {
|
|
|
|
|
const thisUB = upperBounds.exec(othersig)
|
|
|
|
|
if (thisUB) ubTypes.add(thisUB[2])
|
|
|
|
|
let basesig = othersig.replaceAll(templateCall, '')
|
|
|
|
|
if (basesig !== othersig) {
|
|
|
|
|
// A template
|
|
|
|
|
const testsig = substituteInSignature(
|
|
|
|
|
basesig, theTemplateParam, '')
|
|
|
|
|
if (testsig === basesig) {
|
|
|
|
|
// that is not also top-level
|
|
|
|
|
for (const templateType of typeListOfSignature(basesig)) {
|
|
|
|
|
if (templateType.slice(0,3) === '...') {
|
|
|
|
|
templateType = templateType.slice(3)
|
|
|
|
|
}
|
|
|
|
|
ubTypes.add(templateType)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (const othersig in imps) {
|
|
|
|
|
let basesig = othersig.replaceAll(templateCall, '')
|
|
|
|
|
const testsig = substituteInSignature(
|
|
|
|
|
basesig, theTemplateParam, '')
|
|
|
|
|
if (testsig !== basesig) continue // a top-level template
|
|
|
|
|
for (let othertype of typeListOfSignature(othersig)) {
|
|
|
|
|
if (othertype.slice(0,3) === '...') {
|
|
|
|
|
othertype = othertype.slice(3)
|
|
|
|
|
}
|
|
|
|
|
if (this.Types[othertype] === anySpec) continue
|
|
|
|
|
const testType = substituteInSignature(
|
|
|
|
|
othertype, theTemplateParam, '')
|
|
|
|
|
let otherTypeCollection = [othertype]
|
|
|
|
|
if (testType !== othertype) {
|
|
|
|
|
const base = othertype.split('<',1)[0]
|
|
|
|
|
otherTypeCollection = this._instantiationsOf[base]
|
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
instantiationSet.add(convtype)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const instType of instantiationSet) {
|
|
|
|
|
if (!(instType in this.Types)) continue
|
|
|
|
|
if (this.Types[instType] === anySpec) continue
|
|
|
|
@ -785,8 +916,7 @@ export default class PocomathInstance {
|
|
|
|
|
}
|
|
|
|
|
return behavior.does(innerRefs)
|
|
|
|
|
}
|
|
|
|
|
this._addTFimplementation(
|
|
|
|
|
tf_imps, signature, {uses, does: patch})
|
|
|
|
|
this._addTFimplementation(tf_imps, signature, {uses, does: patch})
|
|
|
|
|
tf_imps[signature]._pocoSignature = rawSignature
|
|
|
|
|
tf_imps[signature]._pocoInstance = instType
|
|
|
|
|
behavior.hasInstantiations[instType] = signature
|
|
|
|
@ -794,13 +924,7 @@ export default class PocomathInstance {
|
|
|
|
|
/* Now add the catchall signature */
|
|
|
|
|
/* (Not needed if if it's a bounded template) */
|
|
|
|
|
if (ubType) continue
|
|
|
|
|
if ('_catchall_' in behavior.hasInstantiations) continue
|
|
|
|
|
let templateCall = `<${theTemplateParam}>`
|
|
|
|
|
/* Relying here that the base of 'Foo<T>' is 'Foo': */
|
|
|
|
|
let baseSignature = rawSignature.replaceAll(templateCall, '')
|
|
|
|
|
/* Any remaining template params are top-level */
|
|
|
|
|
const signature = substituteInSignature(
|
|
|
|
|
baseSignature, theTemplateParam, 'ground')
|
|
|
|
|
if (behavior.resolved) continue
|
|
|
|
|
/* The catchall signature has to detect the actual type of the call
|
|
|
|
|
* and add the new instantiations.
|
|
|
|
|
* First, prepare the type inference data:
|
|
|
|
@ -840,150 +964,124 @@ export default class PocomathInstance {
|
|
|
|
|
/* For return type annotation, we may have to fix this to
|
|
|
|
|
propagate the return type. At the moment we are just bagging
|
|
|
|
|
*/
|
|
|
|
|
const patch = (refs) => (...args) => {
|
|
|
|
|
/* We unbundle the rest arg if there is one */
|
|
|
|
|
const regLength = args.length - 1
|
|
|
|
|
if (restParam) {
|
|
|
|
|
const restArgs = args.pop()
|
|
|
|
|
args = args.concat(restArgs)
|
|
|
|
|
}
|
|
|
|
|
/* Now infer the type we actually should have been called for */
|
|
|
|
|
let i = -1
|
|
|
|
|
let j = -1
|
|
|
|
|
/* collect the arg types */
|
|
|
|
|
const argTypes = []
|
|
|
|
|
for (const arg of args) {
|
|
|
|
|
++j
|
|
|
|
|
// in case of rest parameter, reuse last parameter type:
|
|
|
|
|
if (i < inferences.length - 1) ++i
|
|
|
|
|
if (inferences[i]) {
|
|
|
|
|
const argType = inferences[i](arg)
|
|
|
|
|
if (!argType) {
|
|
|
|
|
throw TypeError(
|
|
|
|
|
`Type inference failed for argument ${j} of ${name}`)
|
|
|
|
|
}
|
|
|
|
|
if (argType === 'any') {
|
|
|
|
|
throw TypeError(
|
|
|
|
|
`In call to ${name}, incompatible template arguments: `
|
|
|
|
|
// + args.map(a => JSON.stringify(a)).join(', ')
|
|
|
|
|
// unfortunately barfs on bigints. Need a better formatter
|
|
|
|
|
// wish we could just use the one that console.log uses;
|
|
|
|
|
// is that accessible somehow?
|
|
|
|
|
+ args.map(a => a.toString()).join(', ')
|
|
|
|
|
+ ' of types ' + argTypes.join(', ') + argType)
|
|
|
|
|
}
|
|
|
|
|
argTypes.push(argType)
|
|
|
|
|
const patch = () => {
|
|
|
|
|
const patchFunc = (...tfBundledArgs) => {
|
|
|
|
|
/* We unbundle the rest arg if there is one */
|
|
|
|
|
let args = Array.from(tfBundledArgs)
|
|
|
|
|
const regLength = args.length - 1
|
|
|
|
|
if (restParam) {
|
|
|
|
|
const restArgs = args.pop()
|
|
|
|
|
args = args.concat(restArgs)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (argTypes.length === 0) {
|
|
|
|
|
throw TypeError('Type inference failed for' + name)
|
|
|
|
|
}
|
|
|
|
|
let usedConversions = false
|
|
|
|
|
let instantiateFor = self.joinTypes(argTypes)
|
|
|
|
|
if (instantiateFor === 'any') {
|
|
|
|
|
usedConversions = true
|
|
|
|
|
instantiateFor = self.joinTypes(argTypes, usedConversions)
|
|
|
|
|
/* Now infer the type we actually should have been called for */
|
|
|
|
|
let i = -1
|
|
|
|
|
let j = -1
|
|
|
|
|
/* collect the arg types */
|
|
|
|
|
const argTypes = []
|
|
|
|
|
for (const arg of args) {
|
|
|
|
|
++j
|
|
|
|
|
// in case of rest parameter, reuse last parameter type:
|
|
|
|
|
if (i < inferences.length - 1) ++i
|
|
|
|
|
if (inferences[i]) {
|
|
|
|
|
const argType = inferences[i](arg)
|
|
|
|
|
if (!argType) {
|
|
|
|
|
throw TypeError(
|
|
|
|
|
`Type inference failed for argument ${j} of ${name}`)
|
|
|
|
|
}
|
|
|
|
|
if (argType === 'any') {
|
|
|
|
|
throw TypeError(
|
|
|
|
|
`In call to ${name}, `
|
|
|
|
|
+ 'incompatible template arguments:'
|
|
|
|
|
// + args.map(a => JSON.stringify(a)).join(', ')
|
|
|
|
|
// unfortunately barfs on bigints. Need a better
|
|
|
|
|
// formatter. I wish we could just use the one that
|
|
|
|
|
// console.log uses; is that accessible somehow?
|
|
|
|
|
+ args.map(a => a.toString()).join(', ')
|
|
|
|
|
+ ' of types ' + argTypes.join(', ') + argType)
|
|
|
|
|
}
|
|
|
|
|
argTypes.push(argType)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (argTypes.length === 0) {
|
|
|
|
|
throw TypeError('Type inference failed for' + name)
|
|
|
|
|
}
|
|
|
|
|
let usedConversions = false
|
|
|
|
|
let instantiateFor = self.joinTypes(argTypes)
|
|
|
|
|
if (instantiateFor === 'any') {
|
|
|
|
|
throw TypeError(
|
|
|
|
|
`In call to ${name}, no type unifies arguments `
|
|
|
|
|
+ args.toString() + '; of types ' + argTypes.toString()
|
|
|
|
|
+ '; note each consecutive pair must unify to a '
|
|
|
|
|
+ 'supertype of at least one of them')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const depth = instantiateFor.split('<').length
|
|
|
|
|
if (depth > self._maxDepthSeen) {
|
|
|
|
|
self._maxDepthSeen = depth
|
|
|
|
|
}
|
|
|
|
|
/* Generate the list of actual wanted types */
|
|
|
|
|
const wantTypes = parTypes.map(type => substituteInSignature(
|
|
|
|
|
type, theTemplateParam, instantiateFor))
|
|
|
|
|
const wantSig = wantTypes.join(',')
|
|
|
|
|
/* Now we have to add any actual types that are relevant
|
|
|
|
|
* to this invocation. Namely, that would be every formal parameter
|
|
|
|
|
* type in the invocation, with the parameter template instantiated
|
|
|
|
|
* by instantiateFor, and for all of instantiateFor's "prior types"
|
|
|
|
|
*/
|
|
|
|
|
for (j = 0; j < parTypes.length; ++j) {
|
|
|
|
|
if (wantTypes[j] !== parTypes[j] && parTypes[j].includes('<')) {
|
|
|
|
|
// actually used the param and is a template
|
|
|
|
|
self._ensureTemplateTypes(parTypes[j], instantiateFor)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* Transform the arguments if we used any conversions: */
|
|
|
|
|
if (usedConversions) {
|
|
|
|
|
i = - 1
|
|
|
|
|
for (j = 0; j < args.length; ++j) {
|
|
|
|
|
if (i < parTypes.length - 1) ++i
|
|
|
|
|
let wantType = parTypes[i]
|
|
|
|
|
if (wantType.slice(0,3) === '...') {
|
|
|
|
|
wantType = wantType.slice(3)
|
|
|
|
|
}
|
|
|
|
|
wantType = substituteInSignature(
|
|
|
|
|
wantType, theTemplateParam, instantiateFor)
|
|
|
|
|
if (wantType !== parTypes[i]) {
|
|
|
|
|
args[j] = self._typed.convert(args[j], wantType)
|
|
|
|
|
usedConversions = true
|
|
|
|
|
instantiateFor = self.joinTypes(argTypes, usedConversions)
|
|
|
|
|
if (instantiateFor === 'any') {
|
|
|
|
|
throw TypeError(
|
|
|
|
|
`In call to ${name}, no type unifies arguments `
|
|
|
|
|
+ args.toString() + '; of types ' + argTypes.toString()
|
|
|
|
|
+ '; note each consecutive pair must unify to a '
|
|
|
|
|
+ 'supertype of at least one of them')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* Finally reassemble the rest args if there were any */
|
|
|
|
|
if (restParam) {
|
|
|
|
|
const restArgs = args.slice(regLength)
|
|
|
|
|
args = args.slice(0,regLength)
|
|
|
|
|
args.push(restArgs)
|
|
|
|
|
}
|
|
|
|
|
/* Arrange that the desired instantiation will be there next
|
|
|
|
|
* time so we don't have to go through that again for this type
|
|
|
|
|
*/
|
|
|
|
|
behavior.needsInstantiations.add(instantiateFor)
|
|
|
|
|
self._invalidate(name)
|
|
|
|
|
// And update refs because we now know the type we're instantiating
|
|
|
|
|
// for:
|
|
|
|
|
refs[theTemplateParam] = instantiateFor
|
|
|
|
|
const innerRefs = {}
|
|
|
|
|
for (const dep in simplifiedUses) {
|
|
|
|
|
const simplifiedDep = simplifiedUses[dep]
|
|
|
|
|
if (dep === simplifiedDep) {
|
|
|
|
|
innerRefs[dep] = refs[dep]
|
|
|
|
|
} else {
|
|
|
|
|
let [func, needsig] = dep.split(/[()]/)
|
|
|
|
|
if (self._typed.isTypedFunction(refs[simplifiedDep])) {
|
|
|
|
|
const subsig = substituteInSignature(
|
|
|
|
|
needsig, theTemplateParam, instantiateFor)
|
|
|
|
|
let resname = simplifiedDep
|
|
|
|
|
if (resname == 'self') resname = name
|
|
|
|
|
innerRefs[dep] = self.resolve(
|
|
|
|
|
resname, subsig, refs[simplifiedDep])
|
|
|
|
|
} else {
|
|
|
|
|
innerRefs[dep] = refs[simplifiedDep]
|
|
|
|
|
const depth = instantiateFor.split('<').length
|
|
|
|
|
if (depth > self._maxDepthSeen) {
|
|
|
|
|
self._maxDepthSeen = depth
|
|
|
|
|
}
|
|
|
|
|
/* Generate the list of actual wanted types */
|
|
|
|
|
const wantTypes = parTypes.map(type => substituteInSignature(
|
|
|
|
|
type, theTemplateParam, instantiateFor))
|
|
|
|
|
const wantSig = wantTypes.join(',')
|
|
|
|
|
/* Now we have to add any actual types that are relevant
|
|
|
|
|
* to this invocation. Namely, that would be every formal
|
|
|
|
|
* parameter type in the invocation, with the parameter
|
|
|
|
|
* template instantiated by instantiateFor, and for all of
|
|
|
|
|
* instantiateFor's "prior types"
|
|
|
|
|
*/
|
|
|
|
|
for (j = 0; j < parTypes.length; ++j) {
|
|
|
|
|
if (wantTypes[j] !== parTypes[j] && parTypes[j].includes('<')) {
|
|
|
|
|
// actually used the param and is a template
|
|
|
|
|
self._ensureTemplateTypes(parTypes[j], instantiateFor)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Finally ready to make the call.
|
|
|
|
|
const implementation = behavior.does(innerRefs)
|
|
|
|
|
// We can access return type information here
|
|
|
|
|
// And in particular, if it might be a template, we should try to
|
|
|
|
|
// instantiate it:
|
|
|
|
|
const returnType = returnTypeOf(implementation, wantSig, self)
|
|
|
|
|
for (const possibility of returnType.split('|')) {
|
|
|
|
|
const instantiated = self._maybeInstantiate(possibility)
|
|
|
|
|
if (instantiated) {
|
|
|
|
|
const tempBase = instantiated.split('<',1)[0]
|
|
|
|
|
self._invalidateDependents(':' + tempBase)
|
|
|
|
|
|
|
|
|
|
/* Request the desired instantiation: */
|
|
|
|
|
// But possibly since this resolution was grabbed, the proper
|
|
|
|
|
// instantiation has been added (like if there are multiple
|
|
|
|
|
// uses in the implementation of another method.
|
|
|
|
|
if (!(behavior.needsInstantiations.has(instantiateFor))) {
|
|
|
|
|
behavior.needsInstantiations.add(instantiateFor)
|
|
|
|
|
self._invalidate(name)
|
|
|
|
|
}
|
|
|
|
|
const brandNewMe = self[name]
|
|
|
|
|
const whatToDo = self._typed.resolve(brandNewMe, args)
|
|
|
|
|
// We can access return type information here
|
|
|
|
|
// And in particular, if it might be a template, we should try to
|
|
|
|
|
// instantiate it:
|
|
|
|
|
const returnType = returnTypeOf(whatToDo.fn, wantSig, self)
|
|
|
|
|
for (const possibility of returnType.split('|')) {
|
|
|
|
|
const instantiated = self._maybeInstantiate(possibility)
|
|
|
|
|
if (instantiated) {
|
|
|
|
|
const tempBase = instantiated.split('<',1)[0]
|
|
|
|
|
self._invalidateDependents(':' + tempBase)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (whatToDo === lastWhatToDo) {
|
|
|
|
|
throw new Error(
|
|
|
|
|
`Infinite recursion in resolving $name called on`
|
|
|
|
|
+ args.map(x => x.toString()).join(','))
|
|
|
|
|
}
|
|
|
|
|
lastWhatToDo = whatToDo
|
|
|
|
|
const retval = whatToDo.implementation(...args)
|
|
|
|
|
lastWhatToDo = null
|
|
|
|
|
return retval
|
|
|
|
|
}
|
|
|
|
|
return implementation(...args)
|
|
|
|
|
Object.defineProperty(
|
|
|
|
|
patchFunc, 'name', {value: `${name}(${signature})`})
|
|
|
|
|
return patchFunc
|
|
|
|
|
}
|
|
|
|
|
Object.defineProperty(patch, 'name', {value: `${name}(${signature})`})
|
|
|
|
|
// TODO: Decorate patch with a function that calculates the
|
|
|
|
|
Object.defineProperty(
|
|
|
|
|
patch, 'name', {value: `generate[${name}(${signature})]`})
|
|
|
|
|
// TODO?: Decorate patch with a function that calculates the
|
|
|
|
|
// correct return type a priori. Deferring because unclear what
|
|
|
|
|
// aspects will be merged into typed-function.
|
|
|
|
|
//
|
|
|
|
|
// The actual uses value needs to be a set:
|
|
|
|
|
const outerUses = new Set(Object.values(simplifiedUses))
|
|
|
|
|
this._addTFimplementation(
|
|
|
|
|
tf_imps, signature, {uses: outerUses, does: patch})
|
|
|
|
|
behavior.hasInstantiations._catchall_ = rawSignature
|
|
|
|
|
meta_imps, signature, {uses: new Set(), does: patch})
|
|
|
|
|
behavior.resolved = true
|
|
|
|
|
}
|
|
|
|
|
this._correctPartialSelfRefs(name, tf_imps)
|
|
|
|
|
// Make sure we have all of the needed (template) types; and if they
|
|
|
|
@ -1007,8 +1105,19 @@ export default class PocomathInstance {
|
|
|
|
|
delete fromBehavior.hasInstantiations[imp._pocoInstance]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const tf = this._typed(name, tf_imps)
|
|
|
|
|
Object.defineProperty(tf, 'fromInstance', {value: this})
|
|
|
|
|
let tf
|
|
|
|
|
if (Object.keys(tf_imps).length > 0) {
|
|
|
|
|
tf = this._typed(name, tf_imps)
|
|
|
|
|
Object.defineProperty(tf, 'fromInstance', {value: this})
|
|
|
|
|
}
|
|
|
|
|
let metaTF
|
|
|
|
|
if (Object.keys(meta_imps).length > 0) {
|
|
|
|
|
metaTF = this._metaTyped(name, meta_imps)
|
|
|
|
|
Object.defineProperty(metaTF, 'fromInstance', {value: this})
|
|
|
|
|
}
|
|
|
|
|
this._meta[name] = metaTF
|
|
|
|
|
|
|
|
|
|
tf = tf || metaTF
|
|
|
|
|
Object.defineProperty(this, name, {configurable: true, value: tf})
|
|
|
|
|
return tf
|
|
|
|
|
}
|
|
|
|
@ -1077,7 +1186,10 @@ export default class PocomathInstance {
|
|
|
|
|
'typed-function does not support mixed full and '
|
|
|
|
|
+ 'partial self-reference')
|
|
|
|
|
}
|
|
|
|
|
if (subsetOfKeys(typesOfSignature(needsig), this.Types)) {
|
|
|
|
|
const needTypes = typesOfSignature(needsig)
|
|
|
|
|
const mergedTypes = Object.assign(
|
|
|
|
|
{}, this.Types, this.Templates)
|
|
|
|
|
if (subsetOfKeys(needTypes, mergedTypes)) {
|
|
|
|
|
part_self_references.push(needsig)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1097,10 +1209,19 @@ export default class PocomathInstance {
|
|
|
|
|
* accumulating:
|
|
|
|
|
*/
|
|
|
|
|
if (needsig) {
|
|
|
|
|
const tempTF = this._typed('dummy_' + func, this._TFimps[func])
|
|
|
|
|
let typedUniverse
|
|
|
|
|
let tempTF
|
|
|
|
|
if (Object.keys(this._TFimps[func]).length > 0) {
|
|
|
|
|
typedUniverse = this._typed
|
|
|
|
|
tempTF = typedUniverse('dummy_' + func, this._TFimps[func])
|
|
|
|
|
} else {
|
|
|
|
|
typedUniverse = this._metaTyped
|
|
|
|
|
tempTF = typedUniverse(
|
|
|
|
|
'dummy_' + func, this._metaTFimps[func])
|
|
|
|
|
}
|
|
|
|
|
let result = undefined
|
|
|
|
|
try {
|
|
|
|
|
result = this._typed.find(tempTF, needsig, {exact: true})
|
|
|
|
|
result = typedUniverse.find(tempTF, needsig, {exact: true})
|
|
|
|
|
} catch {}
|
|
|
|
|
if (result) {
|
|
|
|
|
refs[dep] = result
|
|
|
|
@ -1158,7 +1279,7 @@ export default class PocomathInstance {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const implementation = does(refs)
|
|
|
|
|
// could do something with return type information here
|
|
|
|
|
// could do something with return type information here?
|
|
|
|
|
imps[signature] = implementation
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1168,38 +1289,53 @@ export default class PocomathInstance {
|
|
|
|
|
const deferral = imps[aSignature]
|
|
|
|
|
const part_self_references = deferral.psr
|
|
|
|
|
const corrected_self_references = []
|
|
|
|
|
const remaining_self_references = []
|
|
|
|
|
const refs = deferral.builtRefs
|
|
|
|
|
for (const neededSig of part_self_references) {
|
|
|
|
|
// Have to find a match for neededSig among the other signatures
|
|
|
|
|
// of this function. That's a job for typed-function, but we will
|
|
|
|
|
// try here:
|
|
|
|
|
if (neededSig in imps) { // the easy case
|
|
|
|
|
corrected_self_references.push(neededSig)
|
|
|
|
|
remaining_self_references.push(neededSig)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
// No exact match, try to get one that matches with
|
|
|
|
|
// subtypes since the whole conversion thing in typed-function
|
|
|
|
|
// is too complicated to reproduce
|
|
|
|
|
const foundSig = this._findSubtypeImpl(name, imps, neededSig)
|
|
|
|
|
let foundSig = this._findSubtypeImpl(name, imps, neededSig)
|
|
|
|
|
if (foundSig) {
|
|
|
|
|
corrected_self_references.push(foundSig)
|
|
|
|
|
remaining_self_references.push(neededSig)
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error(
|
|
|
|
|
'Implement inexact self-reference in typed-function for '
|
|
|
|
|
+ `${name}(${neededSig})`)
|
|
|
|
|
// Maybe it's a template instance we don't yet have
|
|
|
|
|
foundSig = this._findSubtypeImpl(
|
|
|
|
|
name, this._imps[name], neededSig)
|
|
|
|
|
if (foundSig) {
|
|
|
|
|
const match = this._pocoFindSignature(name, neededSig)
|
|
|
|
|
refs[`self(${neededSig})`] = match.implementation
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error(
|
|
|
|
|
'Implement inexact self-reference in typed-function for '
|
|
|
|
|
+ `${name}(${neededSig})`)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const refs = deferral.builtRefs
|
|
|
|
|
const does = deferral.sigDoes
|
|
|
|
|
imps[aSignature] = this._typed.referTo(
|
|
|
|
|
...corrected_self_references, (...impls) => {
|
|
|
|
|
for (let i = 0; i < part_self_references.length; ++i) {
|
|
|
|
|
refs[`self(${part_self_references[i]})`] = impls[i]
|
|
|
|
|
if (remaining_self_references.length > 0) {
|
|
|
|
|
imps[aSignature] = this._typed.referTo(
|
|
|
|
|
...corrected_self_references, (...impls) => {
|
|
|
|
|
for (let i = 0; i < remaining_self_references.length; ++i) {
|
|
|
|
|
refs[`self(${remaining_self_references[i]})`] = impls[i]
|
|
|
|
|
}
|
|
|
|
|
const implementation = does(refs)
|
|
|
|
|
// What will we do with the return type info in here?
|
|
|
|
|
return implementation
|
|
|
|
|
}
|
|
|
|
|
const implementation = does(refs)
|
|
|
|
|
// What will we do with the return type info in here?
|
|
|
|
|
return implementation
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
imps[aSignature] = does(refs)
|
|
|
|
|
}
|
|
|
|
|
imps[aSignature]._pocoSignature = deferral._pocoSignature
|
|
|
|
|
imps[aSignature]._pocoInstance = deferral._pocoInstance
|
|
|
|
|
}
|
|
|
|
@ -1247,7 +1383,7 @@ export default class PocomathInstance {
|
|
|
|
|
if (wantsType in this.Types) return false
|
|
|
|
|
// OK, need to generate the type from the template
|
|
|
|
|
// Set up refines, before, test, and from
|
|
|
|
|
const newTypeSpec = {refines: base}
|
|
|
|
|
const newTypeSpec = {}
|
|
|
|
|
const maybeFrom = {}
|
|
|
|
|
const template = this.Templates[base].spec
|
|
|
|
|
if (!template) {
|
|
|
|
@ -1255,6 +1391,11 @@ export default class PocomathInstance {
|
|
|
|
|
`Implementor error in instantiateTemplate(${base}, ${instantiator})`)
|
|
|
|
|
}
|
|
|
|
|
const instantiatorSpec = this.Types[instantiator]
|
|
|
|
|
if (instantiatorSpec.refines) {
|
|
|
|
|
this.instantiateTemplate(base, instantiatorSpec.refines)
|
|
|
|
|
// Assuming our templates are covariant, I guess
|
|
|
|
|
newTypeSpec.refines = `${base}<${instantiatorSpec.refines}>`
|
|
|
|
|
}
|
|
|
|
|
let beforeTypes = []
|
|
|
|
|
if (instantiatorSpec.before) {
|
|
|
|
|
beforeTypes = instantiatorSpec.before.map(type => `${base}<${type}>`)
|
|
|
|
@ -1268,18 +1409,15 @@ export default class PocomathInstance {
|
|
|
|
|
if (beforeTypes.length > 0) {
|
|
|
|
|
newTypeSpec.before = beforeTypes
|
|
|
|
|
}
|
|
|
|
|
newTypeSpec.test = template.test(this._typeTests[instantiator])
|
|
|
|
|
const templateTest = template.test(this._typeTests[instantiator])
|
|
|
|
|
newTypeSpec.test = x => (template.base(x) && templateTest(x))
|
|
|
|
|
if (template.from) {
|
|
|
|
|
for (let source in template.from) {
|
|
|
|
|
const instSource = substituteInSignature(
|
|
|
|
|
source, theTemplateParam, instantiator)
|
|
|
|
|
let usesFromParam = false
|
|
|
|
|
for (const word of instSource.split(/[<>]/)) {
|
|
|
|
|
if (word === templateFromParam) {
|
|
|
|
|
usesFromParam = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const testSource = substituteInSignature(
|
|
|
|
|
instSource, templateFromParam, instantiator)
|
|
|
|
|
const usesFromParam = (testSource !== instSource)
|
|
|
|
|
if (usesFromParam) {
|
|
|
|
|
for (const iFrom in instantiatorSpec.from) {
|
|
|
|
|
const finalSource = substituteInSignature(
|
|
|
|
@ -1287,11 +1425,13 @@ export default class PocomathInstance {
|
|
|
|
|
maybeFrom[finalSource] = template.from[source](
|
|
|
|
|
instantiatorSpec.from[iFrom])
|
|
|
|
|
}
|
|
|
|
|
// Assuming all templates are covariant here, I guess...
|
|
|
|
|
for (const subType of this._subtypes[instantiator]) {
|
|
|
|
|
const finalSource = substituteInSignature(
|
|
|
|
|
instSource, templateFromParam, subType)
|
|
|
|
|
maybeFrom[finalSource] = template.from[source](x => x)
|
|
|
|
|
if (testSource !== wantsType) { // subtypes handled with refines
|
|
|
|
|
// Assuming all templates are covariant here, I guess...
|
|
|
|
|
for (const subType of this._subtypes[instantiator]) {
|
|
|
|
|
const finalSource = substituteInSignature(
|
|
|
|
|
instSource, templateFromParam, subType)
|
|
|
|
|
maybeFrom[finalSource] = template.from[source](x => x)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
maybeFrom[instSource] = template.from[source]
|
|
|
|
@ -1303,6 +1443,7 @@ export default class PocomathInstance {
|
|
|
|
|
newTypeSpec.from = maybeFrom
|
|
|
|
|
}
|
|
|
|
|
this.installType(wantsType, newTypeSpec)
|
|
|
|
|
this._instantiationsOf[base].add(wantsType)
|
|
|
|
|
return wantsType
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
@ -1314,7 +1455,7 @@ export default class PocomathInstance {
|
|
|
|
|
const otherTypeList = typeListOfSignature(otherSig)
|
|
|
|
|
if (typeList.length !== otherTypeList.length) continue
|
|
|
|
|
let allMatch = true
|
|
|
|
|
let paramBound = 'ground'
|
|
|
|
|
let paramBound = 'any'
|
|
|
|
|
for (let k = 0; k < typeList.length; ++k) {
|
|
|
|
|
let myType = typeList[k]
|
|
|
|
|
let otherType = otherTypeList[k]
|
|
|
|
@ -1326,8 +1467,7 @@ export default class PocomathInstance {
|
|
|
|
|
otherTypeList[k] = `...${paramBound}`
|
|
|
|
|
otherType = paramBound
|
|
|
|
|
}
|
|
|
|
|
const adjustedOtherType = otherType.replaceAll(
|
|
|
|
|
`<${theTemplateParam}>`, '')
|
|
|
|
|
const adjustedOtherType = otherType.replaceAll(templateCall, '')
|
|
|
|
|
if (adjustedOtherType !== otherType) {
|
|
|
|
|
otherTypeList[k] = adjustedOtherType
|
|
|
|
|
otherType = adjustedOtherType
|
|
|
|
@ -1342,22 +1482,21 @@ export default class PocomathInstance {
|
|
|
|
|
theTemplateParam, paramBound)
|
|
|
|
|
}
|
|
|
|
|
if (otherType === 'any') continue
|
|
|
|
|
if (otherType === 'ground') continue
|
|
|
|
|
if (!(otherType in this.Types)) {
|
|
|
|
|
allMatch = false
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if (myType === otherType
|
|
|
|
|
|| this.isSubtypeOf(myType, otherType)) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if (myType === otherType) continue
|
|
|
|
|
if (otherType in this.Templates) {
|
|
|
|
|
const myBase = myType.split('<',1)[0]
|
|
|
|
|
if (myBase === otherType) continue
|
|
|
|
|
if (this.instantiateTemplate(otherType, myType)) {
|
|
|
|
|
let dummy
|
|
|
|
|
dummy = this[name] // for side effects
|
|
|
|
|
return this._findSubtypeImpl(name, this._imps[name], neededSig)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!(otherType in this.Types)) {
|
|
|
|
|
allMatch = false
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if (this.isSubtypeOf(myType, otherType)) continue
|
|
|
|
|
allMatch = false
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
@ -1373,19 +1512,42 @@ export default class PocomathInstance {
|
|
|
|
|
if (!this._typed.isTypedFunction(typedFunction)) {
|
|
|
|
|
typedFunction = this[name]
|
|
|
|
|
}
|
|
|
|
|
let result = undefined
|
|
|
|
|
const haveTF = this._typed.isTypedFunction(typedFunction)
|
|
|
|
|
if (haveTF) {
|
|
|
|
|
// First try a direct match
|
|
|
|
|
let result
|
|
|
|
|
try {
|
|
|
|
|
result = this._typed.findSignature(typedFunction, sig, {exact: true})
|
|
|
|
|
} catch {
|
|
|
|
|
}
|
|
|
|
|
if (result) return result
|
|
|
|
|
// Next, look ourselves but with subtypes:
|
|
|
|
|
const wantTypes = typeListOfSignature(sig)
|
|
|
|
|
for (const [implSig, details]
|
|
|
|
|
of typedFunction._typedFunctionData.signatureMap) {
|
|
|
|
|
let allMatched = true
|
|
|
|
|
const implTypes = typeListOfSignature(implSig)
|
|
|
|
|
for (let i = 0; i < wantTypes.length; ++i) {
|
|
|
|
|
if (wantTypes[i] == implTypes[i]
|
|
|
|
|
|| this.isSubtypeOf(wantTypes[i], implTypes[i])) continue
|
|
|
|
|
allMatched = false
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if (allMatched) return details
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (result || !(this._imps[name])) return result
|
|
|
|
|
if (!(this._imps[name])) return undefined
|
|
|
|
|
const foundsig = this._findSubtypeImpl(name, this._imps[name], sig)
|
|
|
|
|
if (foundsig) {
|
|
|
|
|
if (haveTF) {
|
|
|
|
|
return this._typed.findSignature(typedFunction, foundsig)
|
|
|
|
|
try {
|
|
|
|
|
return this._typed.findSignature(typedFunction, foundsig)
|
|
|
|
|
} catch {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
return this._metaTyped.findSignature(this._meta[name], foundsig)
|
|
|
|
|
} catch {
|
|
|
|
|
}
|
|
|
|
|
// We have an implementation but not a typed function. Do the best
|
|
|
|
|
// we can:
|
|
|
|
@ -1398,20 +1560,8 @@ export default class PocomathInstance {
|
|
|
|
|
const pseudoImpl = foundImpl.does(needs)
|
|
|
|
|
return {fn: pseudoImpl, implementation: pseudoImpl}
|
|
|
|
|
}
|
|
|
|
|
const wantTypes = typeListOfSignature(sig)
|
|
|
|
|
for (const [implSig, details]
|
|
|
|
|
of typedFunction._typedFunctionData.signatureMap) {
|
|
|
|
|
let allMatched = true
|
|
|
|
|
const implTypes = typeListOfSignature(implSig)
|
|
|
|
|
for (let i = 0; i < wantTypes.length; ++i) {
|
|
|
|
|
if (wantTypes[i] == implTypes[i]
|
|
|
|
|
|| this.isSubtypeOf(wantTypes[i], implTypes[i])) continue
|
|
|
|
|
allMatched = false
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if (allMatched) return details
|
|
|
|
|
}
|
|
|
|
|
// Hmm, no luck. Make sure bundle is up-to-date and retry:
|
|
|
|
|
let result = undefined
|
|
|
|
|
typedFunction = this[name]
|
|
|
|
|
try {
|
|
|
|
|
result = this._typed.findSignature(typedFunction, sig)
|
|
|
|
|