feat: Instantiate instances of template instantiation need for self-reference
Formerly, when a self-reference like `self(Tuple<number>)` was encountered, but the `Tuple<number>` instance of a `Tuple<T>` implementation for this operation had not yet been instantiated, the reference would be fulfilled with a call to the catchall implementation for `Tuple`. Now the necessary instance is instantiated on the spot and referred to instead. This change is used to complete return-type specification for all of the Tuple functions.
This commit is contained in:
parent
be9794fd4c
commit
d83f2a7f23
@ -8,13 +8,8 @@ export const abs = {
|
|||||||
'absquare(T)': baseabsq,
|
'absquare(T)': baseabsq,
|
||||||
'absquare(Complex<T>)': absq
|
'absquare(Complex<T>)': absq
|
||||||
}) => {
|
}) => {
|
||||||
const pm = sqrt.fromInstance
|
|
||||||
if (typeof pm === 'undefined') {
|
|
||||||
// Just checking for the dependencies, return value is irrelevant
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
const midType = returnTypeOf(baseabsq)
|
const midType = returnTypeOf(baseabsq)
|
||||||
const sqrtImp = pm.resolve('sqrt', midType, sqrt)
|
const sqrtImp = sqrt.fromInstance.resolve('sqrt', midType, sqrt)
|
||||||
let retType = returnTypeOf(sqrtImp)
|
let retType = returnTypeOf(sqrtImp)
|
||||||
if (retType.includes('|')) {
|
if (retType.includes('|')) {
|
||||||
// This is a bit of a hack, as it relies on all implementations of
|
// This is a bit of a hack, as it relies on all implementations of
|
||||||
|
@ -9,13 +9,9 @@ export const absquare = {
|
|||||||
// we extract the needed implementation below.
|
// we extract the needed implementation below.
|
||||||
'self(T)': absq
|
'self(T)': absq
|
||||||
}) => {
|
}) => {
|
||||||
const pm = add.fromInstance
|
|
||||||
if (typeof pm === 'undefined') {
|
|
||||||
// Just checking the dependencies, return value irrelevant
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
const midType = returnTypeOf(absq)
|
const midType = returnTypeOf(absq)
|
||||||
const addImp = pm.resolve('add', `${midType},${midType}`, add)
|
const addImp = add.fromInstance.resolve(
|
||||||
|
'add', `${midType},${midType}`, add)
|
||||||
return Returns(
|
return Returns(
|
||||||
returnTypeOf(addImp), z => addImp(absq(z.re), absq(z.im)))
|
returnTypeOf(addImp), z => addImp(absq(z.re), absq(z.im)))
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,76 @@ import {typeListOfSignature, typesOfSignature, subsetOfKeys} from './utils.mjs'
|
|||||||
|
|
||||||
const anySpec = {} // fixed dummy specification of 'any' type
|
const anySpec = {} // fixed dummy specification of 'any' type
|
||||||
|
|
||||||
|
/* Template/signature parsing stuff; should probably be moved to a
|
||||||
|
* separate file, but it's a bit interleaved at the moment
|
||||||
|
*/
|
||||||
|
|
||||||
const theTemplateParam = 'T' // First pass: only allow this one exact parameter
|
const theTemplateParam = 'T' // First pass: only allow this one exact parameter
|
||||||
const restTemplateParam = `...${theTemplateParam}`
|
const restTemplateParam = `...${theTemplateParam}`
|
||||||
const templateCall = `<${theTemplateParam}>`
|
const templateCall = `<${theTemplateParam}>`
|
||||||
const templateFromParam = 'U' // For defining covariant conversions
|
const templateFromParam = 'U' // For defining covariant conversions
|
||||||
|
|
||||||
|
/* returns the pair [base, instance] for a template type. If the type
|
||||||
|
* is not a template, instance is undefined
|
||||||
|
*/
|
||||||
|
const templatePattern = /^\s*([^<\s]*)\s*<\s*(\S*)\s*>\s*$/
|
||||||
|
function splitTemplate(type) {
|
||||||
|
if (!(type.includes('<'))) return [type, undefined]
|
||||||
|
const results = templatePattern.exec(type)
|
||||||
|
return [results[1], results[2]]
|
||||||
|
}
|
||||||
|
/* Returns the instance such that type is template instantiated for that
|
||||||
|
* instance.
|
||||||
|
*/
|
||||||
|
function whichInstance(type, template) {
|
||||||
|
if (template === theTemplateParam) return type
|
||||||
|
if (type === template) return ''
|
||||||
|
if (!(template.includes(templateCall))) {
|
||||||
|
throw new TypeError(
|
||||||
|
`Type ${template} is not a template, so can't produce ${type}`)
|
||||||
|
}
|
||||||
|
const [typeBase, typeInstance] = splitTemplate(type)
|
||||||
|
if (!typeInstance) {
|
||||||
|
throw new TypeError(
|
||||||
|
`Type ${type} not from a template, so isn't instance of ${template}`)
|
||||||
|
}
|
||||||
|
const [tempBase, tempInstance] = splitTemplate(template)
|
||||||
|
if (typeBase !== tempBase) {
|
||||||
|
throw new TypeError(
|
||||||
|
`Type ${type} has wrong top-level base to be instance of ${template}`)
|
||||||
|
}
|
||||||
|
return whichInstance(typeInstance, tempInstance)
|
||||||
|
}
|
||||||
|
/* Same as above, but for signatures */
|
||||||
|
function whichSigInstance(sig, tempsig) {
|
||||||
|
const sigTypes = typeListOfSignature(sig)
|
||||||
|
const tempTypes = typeListOfSignature(tempsig)
|
||||||
|
const sigLength = sigTypes.length
|
||||||
|
if (sigLength === 0) {
|
||||||
|
throw new TypeError("No types in signature, so can't determine instance")
|
||||||
|
}
|
||||||
|
if (sigLength !== tempTypes.length) {
|
||||||
|
throw new TypeError(`Signatures ${sig} and ${tempsig} differ in length`)
|
||||||
|
}
|
||||||
|
let maybeInstance = whichInstance(sigTypes[0], tempTypes[0])
|
||||||
|
for (let i = 1; i < sigLength; ++i) {
|
||||||
|
const currInstance = whichInstance(sigTypes[i], tempTypes[i])
|
||||||
|
if (maybeInstance) {
|
||||||
|
if (currInstance && currInstance !== maybeInstance) {
|
||||||
|
throw new TypeError(
|
||||||
|
`Inconsistent instantiation of ${sig} from ${tempsig}`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
maybeInstance = currInstance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!maybeInstance) {
|
||||||
|
throw new TypeError(
|
||||||
|
`Signature ${sig} identical to ${tempsig}, not an instance`)
|
||||||
|
}
|
||||||
|
return maybeInstance
|
||||||
|
}
|
||||||
|
|
||||||
/* 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
|
||||||
*/
|
*/
|
||||||
@ -202,7 +267,10 @@ export default class PocomathInstance {
|
|||||||
const stdimps = {}
|
const stdimps = {}
|
||||||
for (const [signature, does] of Object.entries(spec)) {
|
for (const [signature, does] of Object.entries(spec)) {
|
||||||
const uses = new Set()
|
const uses = new Set()
|
||||||
|
try {
|
||||||
does(dependencyExtractor(uses))
|
does(dependencyExtractor(uses))
|
||||||
|
} catch {
|
||||||
|
}
|
||||||
stdimps[signature] = {uses, does}
|
stdimps[signature] = {uses, does}
|
||||||
}
|
}
|
||||||
stdFunctions[item] = stdimps
|
stdFunctions[item] = stdimps
|
||||||
@ -334,7 +402,7 @@ export default class PocomathInstance {
|
|||||||
* the corresponding changes to the _typed object immediately
|
* the corresponding changes to the _typed object immediately
|
||||||
*/
|
*/
|
||||||
installType = Returns('void', function(type, spec) {
|
installType = Returns('void', function(type, spec) {
|
||||||
const parts = type.split(/[<,>]/)
|
const parts = type.split(/[<,>]/).map(s => s.trim())
|
||||||
if (this._templateParam(parts[0])) {
|
if (this._templateParam(parts[0])) {
|
||||||
throw new SyntaxError(
|
throw new SyntaxError(
|
||||||
`Type name '${type}' reserved for template parameter`)
|
`Type name '${type}' reserved for template parameter`)
|
||||||
@ -400,14 +468,14 @@ export default class PocomathInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Add the conversion in the metaverse if need be: */
|
/* Add the conversion in the metaverse if need be: */
|
||||||
const toParts = nextSuper.split('<', 2)
|
const [toBase, toInstance] = splitTemplate(nextSuper)
|
||||||
if (toParts.length > 1) {
|
if (toInstance) {
|
||||||
const fromParts = from.split('<', 2)
|
const [fromBase, fromInstance] = splitTemplate(from)
|
||||||
if (fromParts.length === 1 || fromParts[0] !== toParts[0]) {
|
if (!fromBase || fromBase !== toBase) {
|
||||||
this._metafy(from)
|
this._metafy(from)
|
||||||
try {
|
try {
|
||||||
this._metaTyped.addConversion(
|
this._metaTyped.addConversion(
|
||||||
{from, to: toParts[0], convert: spec.from[from]})
|
{from, to: toBase, convert: spec.from[from]})
|
||||||
} catch {
|
} catch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -438,12 +506,12 @@ export default class PocomathInstance {
|
|||||||
})
|
})
|
||||||
this._invalidateDependents(':' + nextSuper)
|
this._invalidateDependents(':' + nextSuper)
|
||||||
/* Add the conversion in the metaverse if need be: */
|
/* Add the conversion in the metaverse if need be: */
|
||||||
const toParts = nextSuper.split('<', 2)
|
const [toBase, toInstance] = splitTemplate(nextSuper)
|
||||||
if (toParts.length > 1 && base !== toParts[0]) {
|
if (toInstance && base !== toBase) {
|
||||||
this._metafy(type)
|
this._metafy(type)
|
||||||
this._metaTyped.addConversion({
|
this._metaTyped.addConversion({
|
||||||
from: type,
|
from: type,
|
||||||
to: toParts[0],
|
to: toBase,
|
||||||
convert: this.Types[to].from[fromtype]
|
convert: this.Types[to].from[fromtype]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -559,7 +627,7 @@ export default class PocomathInstance {
|
|||||||
|
|
||||||
/* Used internally to install a template type */
|
/* Used internally to install a template type */
|
||||||
_installTemplateType(type, spec) {
|
_installTemplateType(type, spec) {
|
||||||
const base = type.split('<')[0]
|
const [base] = splitTemplate(type)
|
||||||
/* For now, just allow a single template per base type; that
|
/* For now, just allow a single template per base type; that
|
||||||
* might need to change later:
|
* might need to change later:
|
||||||
*/
|
*/
|
||||||
@ -760,7 +828,7 @@ export default class PocomathInstance {
|
|||||||
let keep = true
|
let keep = true
|
||||||
for (const type of typesOfSignature(entry[0])) {
|
for (const type of typesOfSignature(entry[0])) {
|
||||||
if (type in this.Types) continue
|
if (type in this.Types) continue
|
||||||
const baseType = type.split('<')[0]
|
const [baseType] = splitTemplate(type)
|
||||||
if (baseType in this.Templates) continue
|
if (baseType in this.Templates) continue
|
||||||
keep = false
|
keep = false
|
||||||
break
|
break
|
||||||
@ -857,7 +925,7 @@ export default class PocomathInstance {
|
|||||||
othertype, theTemplateParam, '')
|
othertype, theTemplateParam, '')
|
||||||
let otherTypeCollection = [othertype]
|
let otherTypeCollection = [othertype]
|
||||||
if (testType !== othertype) {
|
if (testType !== othertype) {
|
||||||
const base = othertype.split('<',1)[0]
|
const [base] = splitTemplate(othertype)
|
||||||
otherTypeCollection = this._instantiationsOf[base]
|
otherTypeCollection = this._instantiationsOf[base]
|
||||||
}
|
}
|
||||||
for (const possibility of otherTypeCollection) {
|
for (const possibility of otherTypeCollection) {
|
||||||
@ -873,43 +941,7 @@ export default class PocomathInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const instType of instantiationSet) {
|
for (const instType of instantiationSet) {
|
||||||
if (!(instType in this.Types)) continue
|
this._instantiateTemplateImplementation(name, rawSignature, instType)
|
||||||
if (this.Types[instType] === anySpec) continue
|
|
||||||
if (instType in behavior.hasInstantiations) continue
|
|
||||||
const signature =
|
|
||||||
substituteInSignature(rawSignature, theTemplateParam, instType)
|
|
||||||
/* Don't override an explicit implementation: */
|
|
||||||
if (signature in imps) continue
|
|
||||||
/* Don't go too deep */
|
|
||||||
let maxdepth = 0
|
|
||||||
for (const argType in typeListOfSignature(signature)) {
|
|
||||||
const depth = argType.split('<').length
|
|
||||||
if (depth > maxdepth) maxdepth = depth
|
|
||||||
}
|
|
||||||
if (maxdepth > this._maxDepthSeen + 1) continue
|
|
||||||
/* All right, go ahead and instantiate */
|
|
||||||
const uses = new Set()
|
|
||||||
for (const dep of behavior.uses) {
|
|
||||||
if (this._templateParam(dep)) continue
|
|
||||||
uses.add(substituteInSignature(dep, theTemplateParam, instType))
|
|
||||||
}
|
|
||||||
const patch = (refs) => {
|
|
||||||
const innerRefs = {}
|
|
||||||
for (const dep of behavior.uses) {
|
|
||||||
if (this._templateParam(dep)) {
|
|
||||||
innerRefs[dep] = instType
|
|
||||||
} else {
|
|
||||||
const outerName = substituteInSignature(
|
|
||||||
dep, theTemplateParam, instType)
|
|
||||||
innerRefs[dep] = refs[outerName]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return behavior.does(innerRefs)
|
|
||||||
}
|
|
||||||
this._addTFimplementation(tf_imps, signature, {uses, does: patch})
|
|
||||||
tf_imps[signature]._pocoSignature = rawSignature
|
|
||||||
tf_imps[signature]._pocoInstance = instType
|
|
||||||
behavior.hasInstantiations[instType] = signature
|
|
||||||
}
|
}
|
||||||
/* Now add the catchall signature */
|
/* Now add the catchall signature */
|
||||||
/* (Not needed if if it's a bounded template) */
|
/* (Not needed if if it's a bounded template) */
|
||||||
@ -1031,7 +1063,7 @@ export default class PocomathInstance {
|
|||||||
for (const possibility of returnType.split('|')) {
|
for (const possibility of returnType.split('|')) {
|
||||||
const instantiated = self._maybeInstantiate(possibility)
|
const instantiated = self._maybeInstantiate(possibility)
|
||||||
if (instantiated) {
|
if (instantiated) {
|
||||||
const tempBase = instantiated.split('<',1)[0]
|
const [tempBase] = splitTemplate(instantiated)
|
||||||
self._invalidateDependents(':' + tempBase)
|
self._invalidateDependents(':' + tempBase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1047,6 +1079,7 @@ export default class PocomathInstance {
|
|||||||
}
|
}
|
||||||
Object.defineProperty(
|
Object.defineProperty(
|
||||||
patchFunc, 'name', {value: `${name}(${signature})`})
|
patchFunc, 'name', {value: `${name}(${signature})`})
|
||||||
|
patchFunc._pocoSignature = rawSignature
|
||||||
return patchFunc
|
return patchFunc
|
||||||
}
|
}
|
||||||
Object.defineProperty(
|
Object.defineProperty(
|
||||||
@ -1083,12 +1116,12 @@ export default class PocomathInstance {
|
|||||||
let tf
|
let tf
|
||||||
if (Object.keys(tf_imps).length > 0) {
|
if (Object.keys(tf_imps).length > 0) {
|
||||||
tf = this._typed(name, tf_imps)
|
tf = this._typed(name, tf_imps)
|
||||||
Object.defineProperty(tf, 'fromInstance', {value: this})
|
tf.fromInstance = this
|
||||||
}
|
}
|
||||||
let metaTF
|
let metaTF
|
||||||
if (Object.keys(meta_imps).length > 0) {
|
if (Object.keys(meta_imps).length > 0) {
|
||||||
metaTF = this._metaTyped(name, meta_imps)
|
metaTF = this._metaTyped(name, meta_imps)
|
||||||
Object.defineProperty(metaTF, 'fromInstance', {value: this})
|
metaTF.fromInstance = this
|
||||||
}
|
}
|
||||||
this._meta[name] = metaTF
|
this._meta[name] = metaTF
|
||||||
|
|
||||||
@ -1137,11 +1170,58 @@ export default class PocomathInstance {
|
|||||||
return undefined // no such type
|
return undefined // no such type
|
||||||
}
|
}
|
||||||
// it's a template type, turn it into a template and an arg
|
// it's a template type, turn it into a template and an arg
|
||||||
let base = type.split('<',1)[0]
|
let [base, arg] = splitTemplate(type)
|
||||||
const arg = type.slice(base.length+1, -1)
|
|
||||||
return this.instantiateTemplate(base, arg)
|
return this.instantiateTemplate(base, arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Generate and include a template instantiation for operation name
|
||||||
|
* for the template signature templateSignature instantiated for
|
||||||
|
* instanceType, returning the resulting implementation.
|
||||||
|
*/
|
||||||
|
_instantiateTemplateImplementation(name, templateSignature, instanceType) {
|
||||||
|
if (!(instanceType in this.Types)) return undefined
|
||||||
|
if (this.Types[instanceType] === anySpec) return undefined
|
||||||
|
const imps = this._imps[name]
|
||||||
|
const behavior = imps[templateSignature]
|
||||||
|
if (instanceType in behavior.hasInstantiations) return undefined
|
||||||
|
const signature = substituteInSignature(
|
||||||
|
templateSignature, theTemplateParam, instanceType)
|
||||||
|
/* Don't override an explicit implementation: */
|
||||||
|
if (signature in imps) return undefined
|
||||||
|
/* Don't go too deep */
|
||||||
|
let maxdepth = 0
|
||||||
|
for (const argType in typeListOfSignature(signature)) {
|
||||||
|
const depth = argType.split('<').length
|
||||||
|
if (depth > maxdepth) maxdepth = depth
|
||||||
|
}
|
||||||
|
if (maxdepth > this._maxDepthSeen + 1) return undefined
|
||||||
|
/* All right, go ahead and instantiate */
|
||||||
|
const uses = new Set()
|
||||||
|
for (const dep of behavior.uses) {
|
||||||
|
if (this._templateParam(dep)) continue
|
||||||
|
uses.add(substituteInSignature(dep, theTemplateParam, instanceType))
|
||||||
|
}
|
||||||
|
const patch = (refs) => {
|
||||||
|
const innerRefs = {}
|
||||||
|
for (const dep of behavior.uses) {
|
||||||
|
if (this._templateParam(dep)) {
|
||||||
|
innerRefs[dep] = instanceType
|
||||||
|
} else {
|
||||||
|
const outerName = substituteInSignature(
|
||||||
|
dep, theTemplateParam, instanceType)
|
||||||
|
innerRefs[dep] = refs[outerName]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return behavior.does(innerRefs)
|
||||||
|
}
|
||||||
|
const tf_imps = this._TFimps[name]
|
||||||
|
this._addTFimplementation(tf_imps, signature, {uses, does: patch})
|
||||||
|
tf_imps[signature]._pocoSignature = templateSignature
|
||||||
|
tf_imps[signature]._pocoInstance = instanceType
|
||||||
|
behavior.hasInstantiations[instanceType] = signature
|
||||||
|
return tf_imps[signature]
|
||||||
|
}
|
||||||
|
|
||||||
/* Adapts Pocomath-style behavior specification (uses, does) for signature
|
/* Adapts Pocomath-style behavior specification (uses, does) for signature
|
||||||
* to typed-function implementations and inserts the result into plain
|
* to typed-function implementations and inserts the result into plain
|
||||||
* object imps
|
* object imps
|
||||||
@ -1254,6 +1334,7 @@ export default class PocomathInstance {
|
|||||||
refs.self = self
|
refs.self = self
|
||||||
const implementation = does(refs)
|
const implementation = does(refs)
|
||||||
Object.defineProperty(implementation, 'name', {value: does.name})
|
Object.defineProperty(implementation, 'name', {value: does.name})
|
||||||
|
implementation.fromInstance = this
|
||||||
// What are we going to do with the return type info in here?
|
// What are we going to do with the return type info in here?
|
||||||
return implementation
|
return implementation
|
||||||
})
|
})
|
||||||
@ -1271,11 +1352,13 @@ export default class PocomathInstance {
|
|||||||
deferred: true,
|
deferred: true,
|
||||||
builtRefs: refs,
|
builtRefs: refs,
|
||||||
sigDoes: does,
|
sigDoes: does,
|
||||||
|
fromInstance: this,
|
||||||
psr: part_self_references
|
psr: part_self_references
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const implementation = does(refs)
|
const implementation = does(refs)
|
||||||
|
implementation.fromInstance = this
|
||||||
// could do something with return type information here?
|
// could do something with return type information here?
|
||||||
imps[signature] = implementation
|
imps[signature] = implementation
|
||||||
}
|
}
|
||||||
@ -1310,7 +1393,22 @@ export default class PocomathInstance {
|
|||||||
name, this._imps[name], neededSig)
|
name, this._imps[name], neededSig)
|
||||||
if (foundSig) {
|
if (foundSig) {
|
||||||
const match = this._pocoFindSignature(name, neededSig)
|
const match = this._pocoFindSignature(name, neededSig)
|
||||||
|
const neededTemplate = match.fn._pocoSignature
|
||||||
|
const neededInstance = whichSigInstance(
|
||||||
|
neededSig, neededTemplate)
|
||||||
|
const neededImplementation =
|
||||||
|
this._instantiateTemplateImplementation(
|
||||||
|
name, neededTemplate, neededInstance)
|
||||||
|
if (!neededImplementation) {
|
||||||
refs[`self(${neededSig})`] = match.implementation
|
refs[`self(${neededSig})`] = match.implementation
|
||||||
|
} else {
|
||||||
|
if (typeof neededImplementation === 'function') {
|
||||||
|
refs[`self(${neededSig})`] = neededImplementation
|
||||||
|
} else {
|
||||||
|
corrected_self_references.push(neededSig)
|
||||||
|
remaining_self_references.push(neededSig)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Implement inexact self-reference in typed-function for '
|
'Implement inexact self-reference in typed-function for '
|
||||||
@ -1335,6 +1433,7 @@ export default class PocomathInstance {
|
|||||||
}
|
}
|
||||||
imps[aSignature]._pocoSignature = deferral._pocoSignature
|
imps[aSignature]._pocoSignature = deferral._pocoSignature
|
||||||
imps[aSignature]._pocoInstance = deferral._pocoInstance
|
imps[aSignature]._pocoInstance = deferral._pocoInstance
|
||||||
|
imps[aSignature].fromInstance = deferral.fromInstance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1343,8 +1442,7 @@ export default class PocomathInstance {
|
|||||||
* in the instance.
|
* in the instance.
|
||||||
*/
|
*/
|
||||||
_ensureTemplateTypes(template, type) {
|
_ensureTemplateTypes(template, type) {
|
||||||
const base = template.split('<', 1)[0]
|
const [base, arg] = splitTemplate(template)
|
||||||
const arg = template.slice(base.length + 1, -1)
|
|
||||||
if (!arg) {
|
if (!arg) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Implementation error in _ensureTemplateTypes', template, type)
|
'Implementation error in _ensureTemplateTypes', template, type)
|
||||||
@ -1481,7 +1579,7 @@ export default class PocomathInstance {
|
|||||||
if (otherType === 'any') continue
|
if (otherType === 'any') continue
|
||||||
if (myType === otherType) continue
|
if (myType === otherType) continue
|
||||||
if (otherType in this.Templates) {
|
if (otherType in this.Templates) {
|
||||||
const myBase = myType.split('<',1)[0]
|
const [myBase] = splitTemplate(myType)
|
||||||
if (myBase === otherType) continue
|
if (myBase === otherType) continue
|
||||||
if (this.instantiateTemplate(otherType, myType)) {
|
if (this.instantiateTemplate(otherType, myType)) {
|
||||||
let dummy
|
let dummy
|
||||||
@ -1525,11 +1623,25 @@ export default class PocomathInstance {
|
|||||||
let allMatched = true
|
let allMatched = true
|
||||||
const implTypes = typeListOfSignature(implSig)
|
const implTypes = typeListOfSignature(implSig)
|
||||||
for (let i = 0; i < wantTypes.length; ++i) {
|
for (let i = 0; i < wantTypes.length; ++i) {
|
||||||
if (wantTypes[i] == implTypes[i]
|
const implIndex = Math.min(i, implTypes.length - 1)
|
||||||
|| this.isSubtypeOf(wantTypes[i], implTypes[i])) continue
|
let implType = implTypes[implIndex]
|
||||||
|
if (implIndex < i) {
|
||||||
|
if (implType.slice(0,3) !== '...') {
|
||||||
|
// ran out of arguments in impl
|
||||||
allMatched = false
|
allMatched = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (implType.slice(0,3) === '...') {
|
||||||
|
implType = implType.slice(3)
|
||||||
|
}
|
||||||
|
const hasMatch = implType.split('|').some(
|
||||||
|
t => (wantTypes[i] === t || this.isSubtypeOf(wantTypes[i], t)))
|
||||||
|
if (!hasMatch) {
|
||||||
|
allMatched = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
if (allMatched) return details
|
if (allMatched) return details
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/* A template type representing a homeogeneous tuple of elements */
|
/* A template type representing a homeogeneous tuple of elements */
|
||||||
import PocomathInstance from '../../core/PocomathInstance.mjs'
|
import PocomathInstance from '../../core/PocomathInstance.mjs'
|
||||||
|
import {Returns, returnTypeOf} from '../../core/Returns.mjs'
|
||||||
|
|
||||||
const Tuple = new PocomathInstance('Tuple')
|
const Tuple = new PocomathInstance('Tuple')
|
||||||
|
|
||||||
@ -33,13 +34,20 @@ Tuple.promoteUnary = {
|
|||||||
'Tuple<T>': ({
|
'Tuple<T>': ({
|
||||||
'self(T)': me,
|
'self(T)': me,
|
||||||
tuple
|
tuple
|
||||||
}) => t => tuple(...(t.elts.map(x => me(x)))) // NOTE: this must use
|
}) => {
|
||||||
// the inner arrow function to drop additional arguments that Array.map
|
const compType = me.fromInstance.joinTypes(
|
||||||
// supplies, as otherwise the wrong signature of `me` might be used.
|
returnTypeOf(me).split('|'), 'convert')
|
||||||
|
return Returns(
|
||||||
|
`Tuple<${compType}>`, t => tuple(...(t.elts.map(x => me(x)))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple.promoteBinaryUnary = {
|
Tuple.promoteBinaryUnary = {
|
||||||
'Tuple<T>,Tuple<T>': ({'self(T,T)': meB, 'self(T)': meU, tuple}) => (s,t) => {
|
'Tuple<T>,Tuple<T>': ({'self(T,T)': meB, 'self(T)': meU, tuple}) => {
|
||||||
|
const compTypes = returnTypeOf(meB).split('|').concat(
|
||||||
|
returnTypeOf(meU).split('|'))
|
||||||
|
const compType = meU.fromInstance.joinTypes(compTypes, 'convert')
|
||||||
|
return Returns(`Tuple<${compType}>`, (s,t) => {
|
||||||
let i = -1
|
let i = -1
|
||||||
let result = []
|
let result = []
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -53,22 +61,30 @@ Tuple.promoteBinaryUnary = {
|
|||||||
else break
|
else break
|
||||||
}
|
}
|
||||||
return tuple(...result)
|
return tuple(...result)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple.promoteBinary = {
|
Tuple.promoteBinary = {
|
||||||
'Tuple<T>,Tuple<T>': ({'self(T,T)': meB, tuple}) => (s,t) => {
|
'Tuple<T>,Tuple<T>': ({'self(T,T)': meB, tuple}) => {
|
||||||
|
const compType = meB.fromInstance.joinTypes(
|
||||||
|
returnTypeOf(meB).split('|'))
|
||||||
|
return Returns(`Tuple<${compType}>`, (s,t) => {
|
||||||
const lim = Math.max(s.elts.length, t.elts.length)
|
const lim = Math.max(s.elts.length, t.elts.length)
|
||||||
const result = []
|
const result = []
|
||||||
for (let i = 0; i < lim; ++i) {
|
for (let i = 0; i < lim; ++i) {
|
||||||
result.push(meB(s.elts[i], t.elts[i]))
|
result.push(meB(s.elts[i], t.elts[i]))
|
||||||
}
|
}
|
||||||
return tuple(...result)
|
return tuple(...result)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple.promoteBinaryStrict = {
|
Tuple.promoteBinaryStrict = {
|
||||||
'Tuple<T>,Tuple<T>': ({'self(T,T)': meB, tuple}) => (s,t) => {
|
'Tuple<T>,Tuple<T>': ({'self(T,T)': meB, tuple}) => {
|
||||||
|
const compType = meB.fromInstance.joinTypes(
|
||||||
|
returnTypeOf(meB).split('|'))
|
||||||
|
return Returns(`Tuple<${compType}>`, (s,t) => {
|
||||||
if (s.elts.length !== t.elts.length) {
|
if (s.elts.length !== t.elts.length) {
|
||||||
throw new RangeError('Tuple length mismatch') // get name of self ??
|
throw new RangeError('Tuple length mismatch') // get name of self ??
|
||||||
}
|
}
|
||||||
@ -77,6 +93,7 @@ Tuple.promoteBinaryStrict = {
|
|||||||
result.push(meB(s.elts[i], t.elts[i]))
|
result.push(meB(s.elts[i], t.elts[i]))
|
||||||
}
|
}
|
||||||
return tuple(...result)
|
return tuple(...result)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export * from './Types/Tuple.mjs'
|
export * from './Types/Tuple.mjs'
|
||||||
|
|
||||||
export const equalTT = {
|
export const equalTT = {
|
||||||
'Tuple<T>,Tuple<T>': ({'self(T,T)': me, 'length(Tuple)': len}) => (s,t) => {
|
'Tuple<T>,Tuple<T>': ({
|
||||||
|
'self(T,T)': me,
|
||||||
|
'length(Tuple)': len
|
||||||
|
}) => Returns('boolean', (s,t) => {
|
||||||
if (len(s) !== len(t)) return false
|
if (len(s) !== len(t)) return false
|
||||||
for (let i = 0; i < len(s); ++i) {
|
for (let i = 0; i < len(s); ++i) {
|
||||||
if (!me(s.elts[i], t.elts[i])) return false
|
if (!me(s.elts[i], t.elts[i])) return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export {Tuple} from './Types/Tuple.mjs'
|
export {Tuple} from './Types/Tuple.mjs'
|
||||||
|
|
||||||
export const isZero = {
|
export const isZero = {
|
||||||
'Tuple<T>': ({'self(T)': me}) => t => t.elts.every(e => me(e))
|
'Tuple<T>': ({'self(T)': me}) => Returns(
|
||||||
|
'boolean', t => t.elts.every(e => me(e)))
|
||||||
// Note we can't just say `every(me)` above since every invokes its
|
// Note we can't just say `every(me)` above since every invokes its
|
||||||
// callback with more arguments, which then violates typed-function's
|
// callback with more arguments, which then violates typed-function's
|
||||||
// signature for `me`
|
// signature for `me`
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export {Tuple} from './Types/Tuple.mjs'
|
export {Tuple} from './Types/Tuple.mjs'
|
||||||
|
|
||||||
/* The purpose of the template argument is to ensure that all of the args
|
/* The purpose of the template argument is to ensure that all of the args
|
||||||
* are convertible to the same type.
|
* are convertible to the same type.
|
||||||
*/
|
*/
|
||||||
export const tuple = {'...T': () => args => ({elts: args})}
|
export const tuple = {
|
||||||
|
'...T': ({T}) => Returns(`Tuple<${T}>`, args => ({elts: args}))
|
||||||
|
}
|
||||||
|
@ -54,6 +54,9 @@ describe('tuple', () => {
|
|||||||
assert.deepStrictEqual(
|
assert.deepStrictEqual(
|
||||||
math.subtract(math.tuple(3n,4n,5n), math.tuple(2n,1n,0n)),
|
math.subtract(math.tuple(3n,4n,5n), math.tuple(2n,1n,0n)),
|
||||||
math.tuple(1n,3n,5n))
|
math.tuple(1n,3n,5n))
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
math.returnTypeOf('subtract', 'Tuple<bigint>,Tuple<bigint>'),
|
||||||
|
'Tuple<bigint>')
|
||||||
assert.throws(
|
assert.throws(
|
||||||
() => math.subtract(math.tuple(5,6), math.tuple(7)),
|
() => math.subtract(math.tuple(5,6), math.tuple(7)),
|
||||||
/RangeError/)
|
/RangeError/)
|
||||||
@ -104,9 +107,16 @@ describe('tuple', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('supports sqrt', () => {
|
it('supports sqrt', () => {
|
||||||
|
const mixedTuple = math.tuple(2, math.complex(0,2), 1.5)
|
||||||
assert.deepStrictEqual(
|
assert.deepStrictEqual(
|
||||||
math.sqrt(math.tuple(4,-4,2.25)),
|
mixedTuple,
|
||||||
math.tuple(2, math.complex(0,2), 1.5))
|
math.tuple(math.complex(2), math.complex(0,2), math.complex(1.5)))
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('tuple', 'NumInt, Complex<NumInt>, number'),
|
||||||
|
'Tuple<Complex<number>>')
|
||||||
|
assert.deepStrictEqual(math.sqrt(math.tuple(4,-4,2.25)), mixedTuple)
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('sqrt', 'Tuple<NumInt>'), 'Tuple<Complex<number>>')
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user