diff --git a/src/core/PocomathInstance.mjs b/src/core/PocomathInstance.mjs index 1fce6b3..0253de0 100644 --- a/src/core/PocomathInstance.mjs +++ b/src/core/PocomathInstance.mjs @@ -7,6 +7,7 @@ import {typesOfSignature, subsetOfKeys} from './utils.mjs' const anySpec = {} // fixed dummy specification of 'any' type 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 * the type @@ -803,11 +804,91 @@ export default class PocomathInstance { 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 - * in the instance + * in the instance. */ _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 } }