feat: Template types #45
@ -7,6 +7,7 @@ import {typesOfSignature, subsetOfKeys} from './utils.mjs'
|
|||||||
const anySpec = {} // fixed dummy specification of 'any' type
|
const anySpec = {} // fixed dummy specification of 'any' type
|
||||||
|
|
||||||
const theTemplateParam = 'T' // First pass: only allow this one exact parameter
|
const theTemplateParam = 'T' // First pass: only allow this one exact parameter
|
||||||
|
const templateFromParam = 'U' // For defining covariant conversions
|
||||||
|
|
||||||
/* Returns a new signature just like sig but with the parameter replaced by
|
/* Returns a new signature just like sig but with the parameter replaced by
|
||||||
* the type
|
* the type
|
||||||
@ -803,11 +804,91 @@ export default class PocomathInstance {
|
|||||||
imps[signature] = does(refs)
|
imps[signature] = does(refs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* HERE!! This function needs to analyze the template and make sure the
|
/* This function analyzes the template and makes sure the
|
||||||
* instantiations of it for type and all prior types of type are present
|
* instantiations of it for type and all prior types of type are present
|
||||||
* in the instance
|
* in the instance.
|
||||||
*/
|
*/
|
||||||
_ensureTemplateTypes(template, type) {
|
_ensureTemplateTypes(template, type) {
|
||||||
|
let [base, arg] = template.split('<', 2)
|
||||||
|
arg = arg.slice(0,-1)
|
||||||
|
if (!arg) {
|
||||||
|
throw new Error(
|
||||||
|
'Implementation error in _ensureTemplateTypes', template, type)
|
||||||
|
}
|
||||||
|
let instantiations
|
||||||
|
if (this._templateParam(arg)) { // 1st-level template
|
||||||
|
instantiations = new Set(this._priorTypes(type))
|
||||||
|
instantiations.add(type)
|
||||||
|
} else { // nested template
|
||||||
|
instantiations = this._ensureTemplateTypes(arg, type)
|
||||||
|
}
|
||||||
|
const resultingTypes = new Set()
|
||||||
|
for (const iType of instantiations) {
|
||||||
|
const resultType = this._maybeAddTemplateType(base, iType)
|
||||||
|
if (resultType) resultingTypes.push(resultType)
|
||||||
|
}
|
||||||
|
return resultingTypes
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Maybe add the instantiation of template type base with argument tyoe
|
||||||
|
* instantiator to the Types of this instance, if it hasn't happened already.
|
||||||
|
* Returns the name of the type if added, false otherwise.
|
||||||
|
*/
|
||||||
|
_maybeAddTemplateType(base, instantiator) {
|
||||||
|
const wantsType = `${base}<${instantiator}>`
|
||||||
|
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 = {}
|
||||||
|
const template = this._templateTypes[base]
|
||||||
|
if (!template) {
|
||||||
|
throw new Error(
|
||||||
|
`Implementor error in _maybeAddTemplateType ${base} ${instantiator}`)
|
||||||
|
}
|
||||||
|
const instantiatorSpec = this.Types[instantiator]
|
||||||
|
if (instantiatorSpec.refines) {
|
||||||
|
// Assuming all templates are covariant, for now
|
||||||
|
newTypeSpec.refines = `${base}<${instantiatorSpec.refines}>`
|
||||||
|
}
|
||||||
|
let beforeTypes = []
|
||||||
|
if (instantiatorSpec.before) {
|
||||||
|
beforeTypes = instantiatorSpec.before.map(type => `${base}<${type}>`)
|
||||||
|
}
|
||||||
|
if (template.before) {
|
||||||
|
for (const beforeTmpl of template.before) {
|
||||||
|
beforeTypes.push(
|
||||||
|
substituteInSig(beforeTmpl, theTemplateParam, instantiator))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (beforeTypes.length > 0) {
|
||||||
|
newTypeSpec.before = beforeTypes
|
||||||
|
}
|
||||||
|
newTypeSpec.test = template.test(instantiatorSpec.test)
|
||||||
|
if (template.from) {
|
||||||
|
newTypeSpec.from = {}
|
||||||
|
for (let source in template.from) {
|
||||||
|
source = substituteInSig(source, theTemplateParam, instantiator)
|
||||||
|
const usesFromParam = false
|
||||||
|
for (const word of source.split(/[<>]/)) {
|
||||||
|
if (word === templateFromParam) {
|
||||||
|
usesFromParam = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (usesFromParam) {
|
||||||
|
for (const iFrom in instantiatorSpec.from) {
|
||||||
|
const finalSource = substituteInSig(
|
||||||
|
source, templateFromParam, iFrom)
|
||||||
|
newTypeSpec[finalSource] = template.from[source](
|
||||||
|
instantiatorSpec.from[iFrom])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newTypeSpec[source] = template.from[source]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.installType(wantsType, newTypeSpec)
|
||||||
|
return wantsType
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user