refactor(Complex): Now a template type!

This means that the real and imaginary parts of a Complex must now be
  the same type. This seems like a real benefit: a Complex with a number real
  part and a bigint imaginary part does not seem sensible.

  Note that this is now straining typed-function in (at least) the following
  ways:
  (1) In this change, it was necessary to remove the logic that the square root
      of a negative number calls complex square root, which then calls back
      to the number square root in its algorithm. (This was creating a circular
      reference in the typed-function which the old implementation of Complex
      was somehow sidestepping.)
  (2) typed-function could not follow conversions that would be allowed by
      uninstantiated templates (e.g. number => Complex<number> if the latter
      template has not been instantiated) and so the facility for
      instantiating a template was surfaced (and for example is called explicitly
      in the demo loader `extendToComplex`. Similarly, this necessitated
      making the unary signature of the `complex` conversion function explicit,
      rather than just via implicit conversion to Complex.
  (3) I find the order of implementations is mattering more in typed-function
      definitions, implying that typed-function's sorting algorithm is having
      trouble distinguishing alternatives.

  But otherwise, the conversion went quite smoothly and I think is a good demo
  of the power of this approach. And I expect that it will work even more
  smoothly if some of the underlying facilities (subtypes, template types) are
  integrated into typed-function.
This commit is contained in:
Glen Whitney 2022-08-06 08:27:44 -07:00
parent 845a2354c9
commit 1444b9828f
25 changed files with 240 additions and 157 deletions

View file

@ -28,6 +28,7 @@ export default class PocomathInstance {
'importDependencies',
'install',
'installType',
'instantiateTemplate',
'joinTypes',
'name',
'self',
@ -436,7 +437,12 @@ export default class PocomathInstance {
}
return
}
// Nothing actually happens until we match a template parameter
// update the typeOf function
const imp = {}
imp[type] = {uses: new Set(['T']), does: ({T}) => () => `${base}<${T}>`}
this._installFunctions({typeOf: imp})
// Nothing else actually happens until we match a template parameter
this.Templates[base] = {type, spec}
}
@ -767,8 +773,9 @@ export default class PocomathInstance {
const subsig = substituteInSig(
needsig, theTemplateParam, instantiateFor)
let resname = simplifiedDep
if (resname === 'self') resname = name
innerRefs[dep] = self._pocoresolve(resname, subsig)
if (resname == 'self') resname = name
innerRefs[dep] = self._pocoresolve(
resname, subsig, refs[simplifiedDep])
} else {
innerRefs[dep] = refs[simplifiedDep]
}
@ -782,7 +789,7 @@ export default class PocomathInstance {
this._addTFimplementation(
tf_imps, signature, {uses: outerUses, does: patch})
}
this._correctPartialSelfRefs(tf_imps)
this._correctPartialSelfRefs(name, tf_imps)
const tf = this._typed(name, tf_imps)
Object.defineProperty(this, name, {configurable: true, value: tf})
return tf
@ -845,7 +852,7 @@ export default class PocomathInstance {
} else {
// can bundle up func, and grab its signature if need be
let destination = this[func]
if (needsig) {
if (destination &&needsig) {
destination = this._pocoresolve(func, needsig)
}
refs[dep] = destination
@ -878,7 +885,7 @@ export default class PocomathInstance {
imps[signature] = does(refs)
}
_correctPartialSelfRefs(imps) {
_correctPartialSelfRefs(name, imps) {
for (const aSignature in imps) {
if (!(imps[aSignature].deferred)) continue
const part_self_references = imps[aSignature].psr
@ -894,7 +901,7 @@ export default class PocomathInstance {
// 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(imps, neededSig)
const foundSig = this._findSubtypeImpl(name, imps, neededSig)
if (foundSig) {
corrected_self_references.push(foundSig)
} else {
@ -936,7 +943,7 @@ export default class PocomathInstance {
}
const resultingTypes = new Set()
for (const iType of instantiations) {
const resultType = this._maybeAddTemplateType(base, iType)
const resultType = this.instantiateTemplate(base, iType)
if (resultType) resultingTypes.add(resultType)
}
return resultingTypes
@ -946,7 +953,7 @@ export default class PocomathInstance {
* 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) {
instantiateTemplate(base, instantiator) {
const wantsType = `${base}<${instantiator}>`
if (wantsType in this.Types) return false
// OK, need to generate the type from the template
@ -956,7 +963,7 @@ export default class PocomathInstance {
const template = this.Templates[base].spec
if (!template) {
throw new Error(
`Implementor error in _maybeAddTemplateType ${base} ${instantiator}`)
`Implementor error in instantiateTemplate(${base}, ${instantiator})`)
}
const instantiatorSpec = this.Types[instantiator]
let beforeTypes = []
@ -1010,7 +1017,7 @@ export default class PocomathInstance {
return wantsType
}
_findSubtypeImpl(imps, neededSig) {
_findSubtypeImpl(name, imps, neededSig) {
if (neededSig in imps) return neededSig
let foundSig = false
const typeList = typeListOfSignature(neededSig)
@ -1047,6 +1054,13 @@ export default class PocomathInstance {
|| this._subtypes[otherType].has(myType)) {
continue
}
if (otherType in this.Templates) {
if (this.instantiateTemplate(otherType, myType)) {
let dummy
dummy = this[name] // for side effects
return this._findSubtypeImpl(name, this._imps[name], neededSig)
}
}
allMatch = false
break
}
@ -1058,17 +1072,28 @@ export default class PocomathInstance {
return foundSig
}
_pocoresolve(name, sig) {
const typedfunc = this[name]
_pocoresolve(name, sig, typedFunction) {
if (!this._typed.isTypedFunction(typedFunction)) {
typedFunction = this[name]
}
let result = undefined
try {
result = this._typed.find(typedfunc, sig, {exact: true})
result = this._typed.find(typedFunction, sig, {exact: true})
} catch {
}
if (result) return result
const foundsig = this._findSubtypeImpl(this._imps[name], sig)
if (foundsig) return this._typed.find(typedfunc, foundsig)
return this._typed.find(typedfunc, sig)
const foundsig = this._findSubtypeImpl(name, this._imps[name], sig)
if (foundsig) return this._typed.find(typedFunction, foundsig)
// Make sure bundle is up-to-date:
typedFunction = this[name]
try {
result = this._typed.find(typedFunction, sig)
} catch {
}
if (result) return result
// total punt, revert to typed-function resolution on every call;
// hopefully this happens rarely:
return typedFunction
}
}