feat(polynomialRoot): Progress commit, quadratic implemented

This commit is contained in:
Glen Whitney 2022-11-30 08:55:12 -05:00
parent de52c041e5
commit c4b1ac9045
9 changed files with 422 additions and 163 deletions

7
src/complex/isReal.mjs Normal file
View File

@ -0,0 +1,7 @@
import Returns from '../core/Returns.mjs'
export * from './Types/Complex.mjs'
export const isReal = {
'Complex<T>': ({'equal(T,T)': eq, 'add(T,T)': plus}) => Returns(
'boolean', z => eq(z.re, plus(z.re, z.im)))
}

View File

@ -18,5 +18,6 @@ export {quaternion} from './quaternion.mjs'
export {quotient} from './quotient.mjs' export {quotient} from './quotient.mjs'
export {roundquotient} from './roundquotient.mjs' export {roundquotient} from './roundquotient.mjs'
export {sqrt} from './sqrt.mjs' export {sqrt} from './sqrt.mjs'
export {sqrtc} from './sqrtc.mjs'
export {zero} from './zero.mjs' export {zero} from './zero.mjs'

View File

@ -0,0 +1,65 @@
import Returns from '../core/Returns.mjs'
export * from './Types/Complex.mjs'
export const polynomialRoot = {
'Complex<T>,...Complex<T>': ({
T,
'tuple(...Complex<T>)': tupCplx,
'tuple(...T)': tupReal,
'isZero(Complex<T>)': zero,
'complex(T)': C,
'multiply(Complex<T>,Complex<T>)': mul,
'divide(Complex<T>,Complex<T>)': div,
'negate(Complex<T>)': neg,
'isReal(Complex<T>)': real,
'equalTT(Complex<T>, Complex<T>)': eq,
'subtract(Complex<T>, Complex<T>)': sub,
'sqrtc(Complex<T>)': sqt,
}) => Returns(`Tuple<${T}>|Tuple<Complex<${T}>>`, (constant, rest) => {
// helper to convert results to appropriate tuple type
const typedTup = arr => {
if (arr.every(real)) {
return tupReal.apply(tupReal, arr.map(z => z.re))
}
return tupCplx.apply(tupCplx, arr)
}
const coeffs = [constant, ...rest]
while (coeffs.length > 0 && zero(coeffs[coeffs.length - 1])) {
coeffs.pop()
}
if (coeffs.length < 2) {
}
switch (coeffs.length) {
case 0: case 1:
throw new RangeError(
`Polynomial [${constant}, ${rest}] must have at least one`
+ 'non-zero non-constant coefficient')
case 2: // linear
return typedTup([neg(div(coeffs[0], coeffs[1]))])
case 3: { // quadratic
const [c, b, a] = coeffs
console.log('solving', a, b, c)
const denom = mul(C(2), a)
const d1 = mul(b, b)
const d2 = mul(C(4), mul(a, c))
console.log('Whoa', denom, d1, d2)
if (eq(d1, d2)) {
console.log('Hello', b, denom, div(neg(b), denom))
return typedTup([div(neg(b), denom)])
}
let discriminant = sqt(sub(d1, d2))
console.log('Uhoh', discriminant)
console.log('Roots', div(sub(discriminant, b), denom), div(sub(neg(discriminant), b), denom))
return typedTup([
div(sub(discriminant, b), denom),
div(sub(neg(discriminant), b), denom)
])
}
default:
throw new RangeError(
'only implemented for cubic or lower-order polynomials, '
+ `not ${JSON.stringify(coeffs)}`)
}
})
}

View File

@ -4,49 +4,30 @@ export * from './Types/Complex.mjs'
export const sqrt = { export const sqrt = {
'Complex<T>': ({ 'Complex<T>': ({
config, config,
'sqrtc(Complex<T>)': predictableSqrt,
'isZero(T)': isZ, 'isZero(T)': isZ,
'sign(T)': sgn,
'one(T)': uno,
'add(T,T)': plus,
'complex(T)': cplxU,
'complex(T,T)': cplxB,
'multiply(T,T)': mult,
'self(T)': me,
'divide(T,T)': div,
'absquare(Complex<T>)': absqC,
'subtract(T,T)': sub
}) => { }) => {
let baseReturns = returnTypeOf(me) if (config.checkingDependency) return undefined
if (baseReturns.includes('|')) { const complexReturns = returnTypeOf(predictableSqrt)
// Bit of a hack, because it is relying on other implementations const baseReturns = complexReturns.slice(8, -1); // Complex<WhatWeWant>
// to list the "typical" value of sqrt first
baseReturns = baseReturns.split('|', 1)[0]
}
if (config.predictable) { if (config.predictable) {
return Returns(`Complex<${baseReturns}>`, z => { return Returns(complexReturns, z => predictableSqrt(z))
const reOne = uno(z.re)
if (isZ(z.im) && sgn(z.re) === reOne) return cplxU(me(z.re))
const reTwo = plus(reOne, reOne)
const myabs = me(absqC(z))
return cplxB(
mult(sgn(z.im), me(div(plus(myabs, z.re), reTwo))),
me(div(sub(myabs, z.re), reTwo))
)
})
} }
return Returns( return Returns(
`Complex<${baseReturns}>|${baseReturns}|undefined`, `Complex<${baseReturns}>|${baseReturns}|undefined`,
z => { z => {
const reOne = uno(z.re) let complexSqrt
if (isZ(z.im) && sgn(z.re) === reOne) return me(z.re) try {
const reTwo = plus(reOne, reOne) complexSqrt = predictableSqrt(z)
const myabs = me(absqC(z)) } catch (e) {
const reSqrt = me(div(plus(myabs, z.re), reTwo)) return undefined
const imSqrt = me(div(sub(myabs, z.re), reTwo)) }
if (reSqrt === undefined || imSqrt === undefined) return undefined if (complexSqrt.re === undefined || complexSqrt.im === undefined) {
return cplxB(mult(sgn(z.im), reSqrt), imSqrt) return undefined
}
if (isZ(complexSqrt.im)) return complexSqrt.re
return complexSqrt
} }
) )
} }

42
src/complex/sqrtc.mjs Normal file
View File

@ -0,0 +1,42 @@
import {Returns, returnTypeOf} from '../core/Returns.mjs'
export * from './Types/Complex.mjs'
export const sqrtc = {
'Complex<T>': ({
T,
'isZero(T)': isZ,
'sign(T)': sgn,
'one(T)': uno,
'add(T,T)': plus,
'complex(T)': cplxU,
'complex(T,T)': cplxB,
'multiply(T,T)': mult,
'sqrt(T)': sqt,
'divide(T,T)': div,
'absquare(Complex<T>)': absqC,
'subtract(T,T)': sub
}) => {
if (isZ.checkingDependency) return undefined
let baseReturns = returnTypeOf(sqt)
if (baseReturns.includes('|')) {
// Bit of a hack, because it is relying on other implementations
// to list the "typical" value of sqrt first
baseReturns = baseReturns.split('|', 1)[0]
}
return Returns(`Complex<${baseReturns}>`, z => {
const reOne = uno(z.re)
if (isZ(z.im) && sgn(z.re) === reOne) return cplxU(sqt(z.re))
const myabs = sqt(absqC(z))
const reTwo = plus(reOne, reOne)
const reQuot = div(plus(myabs, z.re), reTwo)
const imQuot = div(sub(myabs, z.re), reTwo)
if (reQuot === undefined || imQuot === undefined) {
throw new TypeError(`Cannot compute sqrt of ${z.re} + {z.im}i`)
}
return cplxB(
mult(sgn(z.im), sqt(div(plus(myabs, z.re), reTwo))),
sqt(div(sub(myabs, z.re), reTwo))
)
})
}
}

View File

@ -7,6 +7,12 @@ import {typeListOfSignature, typesOfSignature, subsetOfKeys} from './utils.mjs'
const anySpec = {} // fixed dummy specification of 'any' type const anySpec = {} // fixed dummy specification of 'any' type
/* Like `.some(predicate)` but for collections */
function exists(collection, predicate) {
for (const item of collection) if (predicate(item)) return true;
return false;
}
/* Template/signature parsing stuff; should probably be moved to a /* Template/signature parsing stuff; should probably be moved to a
* separate file, but it's a bit interleaved at the moment * separate file, but it's a bit interleaved at the moment
*/ */
@ -98,6 +104,7 @@ export default class PocomathInstance {
static reserved = new Set([ static reserved = new Set([
'chain', 'chain',
'config', 'config',
'convert',
'importDependencies', 'importDependencies',
'install', 'install',
'installType', 'installType',
@ -138,8 +145,21 @@ export default class PocomathInstance {
const me = this const me = this
this._typed.onMismatch = (name, args, sigs) => { this._typed.onMismatch = (name, args, sigs) => {
if (me._invalid.has(name)) { if (me._invalid.has(name)) {
if (this._fixing === name) {
this._fixingCount += 1
if (this._fixingCount > this._maxDepthSeen + 2) {
throw new ReferenceError(
`Infinite descent rebuilding ${name} on ${args}`)
}
} else {
this._fixingCount = 0
}
// rebuild implementation and try again // rebuild implementation and try again
return me[name](...args) const lastFixing = this._fixing
this._fixing = name
const value = me[name](...args)
this._fix = lastFixing
return value
} }
const metaversion = me._meta[name] const metaversion = me._meta[name]
if (metaversion) { if (metaversion) {
@ -185,6 +205,8 @@ export default class PocomathInstance {
this._plainFunctions = new Set() // the names of the plain functions this._plainFunctions = new Set() // the names of the plain functions
this._chainRepository = {} // place to store chainified functions this._chainRepository = {} // place to store chainified functions
this.joinTypes = this.joinTypes.bind(me) this.joinTypes = this.joinTypes.bind(me)
// Provide access to typed function conversion:
this.convert = this._typed.convert.bind(this._typed)
} }
/** /**
@ -763,45 +785,39 @@ export default class PocomathInstance {
/** /**
* Reset an operation to require creation of typed-function, * Reset an operation to require creation of typed-function,
* and if it has no implementations so far, set them up. * and if it has no implementations so far, set them up.
* name is the name of the operation, badType is a type that has been
* invalidated, and reasons is a set of specific operations/signatures
* that have been invalidated
*/ */
_invalidate(name, reason) { _invalidate(name, badType = '', reasons = new Set()) {
if (!(name in this._imps)) { if (!(name in this._imps)) {
this._imps[name] = {} this._imps[name] = {}
this._TFimps[name] = {} this._TFimps[name] = {}
this._metaTFimps[name] = {} this._metaTFimps[name] = {}
} }
if (reason) { // Go through each TF imp and invalidate it if need be
// Make sure no TF imps that depend on reason remain: for (const [signature, imp] of Object.entries(this._TFimps[name])) {
for (const [signature, behavior] of Object.entries(this._imps[name])) { if (imp.deferred
let invalidated = false || (badType && signature.includes(badType))
if (reason.charAt(0) === ':') { || exists(imp.uses, dep => {
const badType = reason.slice(1) const [func, sig] = dep.split(/[()]/)
if (signature.includes(badType)) invalidated = true return reasons.has(dep)
} else { || (reasons.has(func) && !(sig in this._TFimps[func]))
for (const dep of behavior.uses) { })) {
if (dep.includes(reason)) { // Invalidate this implementation:
invalidated = true delete this._TFimps[name][signature]
break const behavior = imp.fromBehavior
}
}
}
if (invalidated) {
if (behavior.explicit) { if (behavior.explicit) {
if (behavior.resolved) delete this._TFimps[signature]
behavior.resolved = false behavior.resolved = false
} else { } else {
for (const fullSig delete behavior.hasInstantiations[imp.instance]
of Object.values(behavior.hasInstantiations)) {
delete this._TFimps[fullSig]
}
behavior.hasInstantiations = {}
}
} }
reasons.add(`${name}(${signature})`)
} }
} }
if (this._invalid.has(name)) return if (this._invalid.has(name)) return
this._invalid.add(name) this._invalid.add(name)
this._invalidateDependents(name) this._invalidateDependents(name, badType, reasons)
const self = this const self = this
Object.defineProperty(this, name, { Object.defineProperty(this, name, {
configurable: true, configurable: true,
@ -815,11 +831,14 @@ export default class PocomathInstance {
/** /**
* Invalidate all the dependents of a given property of the instance * Invalidate all the dependents of a given property of the instance
* reasons is a set of invalidated signatures
*/ */
_invalidateDependents(name) { _invalidateDependents(name, badType, reasons = new Set()) {
if (name.charAt(0) === ':') badType = name.slice(1)
else reasons.add(name)
if (name in this._affects) { if (name in this._affects) {
for (const ancestor of this._affects[name]) { for (const ancestor of this._affects[name]) {
this._invalidate(ancestor, name) this._invalidate(ancestor, badType, reasons)
} }
} }
} }
@ -860,7 +879,7 @@ export default class PocomathInstance {
for (const [rawSignature, behavior] of usableEntries) { for (const [rawSignature, behavior] of usableEntries) {
if (behavior.explicit) { if (behavior.explicit) {
if (!(behavior.resolved)) { if (!(behavior.resolved)) {
this._addTFimplementation(tf_imps, rawSignature, behavior) this._addTFimplementation(name, tf_imps, rawSignature, behavior)
tf_imps[rawSignature]._pocoSignature = rawSignature tf_imps[rawSignature]._pocoSignature = rawSignature
behavior.resolved = true behavior.resolved = true
} }
@ -880,11 +899,18 @@ export default class PocomathInstance {
} }
/* First, add the known instantiations, gathering all types needed */ /* First, add the known instantiations, gathering all types needed */
if (ubType) behavior.needsInstantiations.add(ubType) if (ubType) behavior.needsInstantiations.add(ubType)
const nargs = typeListOfSignature(rawSignature).length
let instantiationSet = new Set() let instantiationSet = new Set()
const ubTypes = new Set() const ubTypes = new Set()
if (!ubType) { if (!ubType) {
// Collect all upper-bound types for this signature // Collect all upper-bound types for this signature
for (const othersig in imps) { for (const othersig in imps) {
const otherNargs = typeListOfSignature(othersig).length
if (nargs !== otherNargs) {
// crude criterion that it won't match, that ignores
// rest args, but hopefully OK for prototype
continue
}
const thisUB = upperBounds.exec(othersig) const thisUB = upperBounds.exec(othersig)
if (thisUB) ubTypes.add(thisUB[2]) if (thisUB) ubTypes.add(thisUB[2])
let basesig = othersig.replaceAll(templateCall, '') let basesig = othersig.replaceAll(templateCall, '')
@ -914,7 +940,6 @@ export default class PocomathInstance {
} }
} }
} }
/* Prevent other existing signatures from blocking use of top-level /* Prevent other existing signatures from blocking use of top-level
* templates via conversions: * templates via conversions:
*/ */
@ -953,7 +978,8 @@ export default class PocomathInstance {
} }
} }
for (const instType of instantiationSet) { for (const instType of instantiationSet) {
this._instantiateTemplateImplementation(name, rawSignature, instType) this._instantiateTemplateImplementation(
name, rawSignature, instType)
} }
/* 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) */
@ -1008,6 +1034,7 @@ export default class PocomathInstance {
`Type inference failed for argument ${j} of ${name}`) `Type inference failed for argument ${j} of ${name}`)
} }
if (argType === 'any') { if (argType === 'any') {
console.log('INCOMPATIBLE ARGUMENTS are', args)
throw TypeError( throw TypeError(
`In call to ${name}, ` `In call to ${name}, `
+ 'incompatible template arguments:' + 'incompatible template arguments:'
@ -1065,12 +1092,19 @@ export default class PocomathInstance {
// But possibly since this resolution was grabbed, the proper // But possibly since this resolution was grabbed, the proper
// instantiation has been added (like if there are multiple // instantiation has been added (like if there are multiple
// uses in the implementation of another method. // uses in the implementation of another method.
if (!(behavior.needsInstantiations.has(instantiateFor))) { let whatToDo
behavior.needsInstantiations.add(instantiateFor) if (!(instantiateFor in behavior.hasInstantiations)) {
const newImp = self._instantiateTemplateImplementation(
name, rawSignature, instantiateFor)
if (newImp) {
whatToDo = {fn: newImp, implementation: newImp}
}
self._invalidate(name) self._invalidate(name)
} }
const brandNewMe = self[name] const brandNewMe = self[name]
const whatToDo = self._typed.resolve(brandNewMe, args) const betterToDo = self._typed.resolve(brandNewMe, args)
whatToDo = betterToDo || whatToDo
// We can access return type information here // We can access return type information here
// And in particular, if it might be a template, we should try to // And in particular, if it might be a template, we should try to
// instantiate it: // instantiate it:
@ -1084,8 +1118,13 @@ export default class PocomathInstance {
} }
if (whatToDo === lastWhatToDo) { if (whatToDo === lastWhatToDo) {
throw new Error( throw new Error(
`Infinite recursion in resolving $name called on` `Infinite recursion in resolving ${name} called on `
+ args.map(x => x.toString()).join(',')) + args.map(x =>
(typeof x === 'object'
? JSON.stringify(x)
: x.toString())
).join(', ')
+ ` inferred to be ${wantSig}`)
} }
lastWhatToDo = whatToDo lastWhatToDo = whatToDo
const retval = whatToDo.implementation(...args) const retval = whatToDo.implementation(...args)
@ -1103,7 +1142,9 @@ export default class PocomathInstance {
// correct return type a priori. Deferring because unclear what // correct return type a priori. Deferring because unclear what
// aspects will be merged into typed-function. // aspects will be merged into typed-function.
this._addTFimplementation( this._addTFimplementation(
meta_imps, signature, {uses: new Set(), does: patch}) name, meta_imps, signature,
{uses: new Set(), does: patch},
behavior)
behavior.resolved = true behavior.resolved = true
} }
this._correctPartialSelfRefs(name, tf_imps) this._correctPartialSelfRefs(name, tf_imps)
@ -1112,6 +1153,9 @@ export default class PocomathInstance {
// ditch the signature: // ditch the signature:
const badSigs = new Set() const badSigs = new Set()
for (const sig in tf_imps) { for (const sig in tf_imps) {
if (!tf_imps[sig].uses) {
throw new ReferenceError(`MONKEY WRENCH: ${name} ${sig}`)
}
for (const type of typeListOfSignature(sig)) { for (const type of typeListOfSignature(sig)) {
if (this._maybeInstantiate(type) === undefined) { if (this._maybeInstantiate(type) === undefined) {
badSigs.add(sig) badSigs.add(sig)
@ -1232,10 +1276,12 @@ export default class PocomathInstance {
return behavior.does(innerRefs) return behavior.does(innerRefs)
} }
const tf_imps = this._TFimps[name] const tf_imps = this._TFimps[name]
this._addTFimplementation(tf_imps, signature, {uses, does: patch}) this._addTFimplementation(
name, tf_imps, signature, {uses, does: patch}, behavior, instanceType)
tf_imps[signature]._pocoSignature = templateSignature tf_imps[signature]._pocoSignature = templateSignature
tf_imps[signature]._pocoInstance = instanceType tf_imps[signature]._pocoInstance = instanceType
behavior.hasInstantiations[instanceType] = signature behavior.hasInstantiations[instanceType] = signature
behavior.needsInstantiations.add(instanceType) // once we have it, keep it
return tf_imps[signature] return tf_imps[signature]
} }
@ -1243,10 +1289,17 @@ export default class PocomathInstance {
* to typed-function implementations and inserts the result into plain * to typed-function implementations and inserts the result into plain
* object imps * object imps
*/ */
_addTFimplementation(imps, signature, behavior) { _addTFimplementation(
const {uses, does} = behavior name, imps, signature, specificBehavior, fromImp, asInstance)
{
if (!fromImp) fromImp = specificBehavior
const {uses, does} = specificBehavior
if (uses.length === 0) { if (uses.length === 0) {
const implementation = does() const implementation = does()
implementation.uses = uses
implementation.fromInstance = this
implementation.fromBehavior = fromImp
implementation.instance = asInstance
// could do something with return type information here // could do something with return type information here
imps[signature] = implementation imps[signature] = implementation
return return
@ -1274,7 +1327,8 @@ export default class PocomathInstance {
*/ */
if (needsig in imps && typeof imps[needsig] == 'function') { if (needsig in imps && typeof imps[needsig] == 'function') {
refs[dep] = imps[needsig] refs[dep] = imps[needsig]
} else { continue
}
if (full_self_referential) { if (full_self_referential) {
throw new SyntaxError( throw new SyntaxError(
'typed-function does not support mixed full and ' 'typed-function does not support mixed full and '
@ -1284,8 +1338,14 @@ export default class PocomathInstance {
const mergedTypes = Object.assign( const mergedTypes = Object.assign(
{}, this.Types, this.Templates) {}, this.Types, this.Templates)
if (subsetOfKeys(needTypes, mergedTypes)) { if (subsetOfKeys(needTypes, mergedTypes)) {
part_self_references.push(needsig) func = name // just resolve it in limbo
} } else {
// uses an unknown type, so will get an undefined impl
console.log(
'WARNING: partial self-reference for', name, 'to',
needsig, 'uses an unknown type')
refs[dep] = undefined
continue
} }
} else { } else {
if (part_self_references.length) { if (part_self_references.length) {
@ -1294,35 +1354,21 @@ export default class PocomathInstance {
+ 'partial self-reference') + 'partial self-reference')
} }
full_self_referential = true full_self_referential = true
continue
}
} }
} else {
if (this[func] === 'limbo') { if (this[func] === 'limbo') {
/* We are in the midst of bundling func */ /* We are in the midst of bundling func (which may be ourself) */
let fallback = true
/* So the first thing we can do is try the tf_imps we are /* So the first thing we can do is try the tf_imps we are
* accumulating: * accumulating:
*/ */
if (needsig) { if (needsig) {
let typedUniverse const candidate = this.resolve(func, needsig)
let tempTF if (typeof candidate === 'function') {
if (Object.keys(this._TFimps[func]).length > 0) { refs[dep] = candidate
typedUniverse = this._typed continue
tempTF = typedUniverse('dummy_' + func, this._TFimps[func])
} else {
typedUniverse = this._metaTyped
tempTF = typedUniverse(
'dummy_' + func, this._metaTFimps[func])
}
let result = undefined
try {
result = typedUniverse.find(tempTF, needsig, {exact: true})
} catch {}
if (result) {
refs[dep] = result
fallback = false
} }
} }
if (fallback) {
/* Either we need the whole function or the signature /* Either we need the whole function or the signature
* we need is not available yet, so we have to use * we need is not available yet, so we have to use
* an indirect reference to func. And given that, there's * an indirect reference to func. And given that, there's
@ -1335,11 +1381,11 @@ export default class PocomathInstance {
Object.defineProperty(redirect, 'name', {value: func}) Object.defineProperty(redirect, 'name', {value: func})
Object.defineProperty(redirect, 'fromInstance', {value: this}) Object.defineProperty(redirect, 'fromInstance', {value: this})
refs[dep] = redirect refs[dep] = redirect
continue
} }
} else {
// can bundle up func, and grab its signature if need be // can bundle up func, and grab its signature if need be
let destination = this[func] let destination = this[func]
if (destination && needsig) { if (needsig) {
destination = this.resolve(func, needsig) destination = this.resolve(func, needsig)
} }
if (!destination) { if (!destination) {
@ -1348,21 +1394,29 @@ export default class PocomathInstance {
// but let's warn. // but let's warn.
console.log( console.log(
'WARNING: No definition found for dependency', 'WARNING: No definition found for dependency',
dep, 'needed by a function with signature', signature) dep, 'needed by', name, '(', signature, ')')
} }
refs[dep] = destination refs[dep] = destination
} }
}
}
if (full_self_referential) { if (full_self_referential) {
imps[signature] = this._typed.referToSelf(self => { imps[signature] = this._typed.referToSelf(self => {
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 implementation.fromInstance = this
implementation.uses = uses
implementation.instance = asInstance
implementation.fromBehavior = fromImp
// 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
}) })
imps[signature].uses = uses
if (!uses) {
throw new ReferenceError(`NANH uses for ${signature} from ${fromImp}`)
}
imps[signature].fromInstance = this
imps[signature].instance = asInstance
imps[signature].fromBehavior = fromImp
return return
} }
if (part_self_references.length) { if (part_self_references.length) {
@ -1377,13 +1431,19 @@ export default class PocomathInstance {
deferred: true, deferred: true,
builtRefs: refs, builtRefs: refs,
sigDoes: does, sigDoes: does,
sigUses: uses,
fromInstance: this, fromInstance: this,
fromBehavior: fromImp,
instance: asInstance,
psr: part_self_references psr: part_self_references
} }
return return
} }
const implementation = does(refs) const implementation = does(refs)
implementation.fromInstance = this implementation.fromInstance = this
implementation.fromBehavior = fromImp
implementation.instance = asInstance
implementation.uses = uses
// could do something with return type information here? // could do something with return type information here?
imps[signature] = implementation imps[signature] = implementation
} }
@ -1457,6 +1517,10 @@ export default class PocomathInstance {
refs[`self(${remaining_self_references[i]})`] = impls[i] refs[`self(${remaining_self_references[i]})`] = impls[i]
} }
const implementation = does(refs) const implementation = does(refs)
implementation.fromInstance = deferral.fromInstance
implementation.fromBehavior = deferral.fromBehavior
implementation.instance = deferral.instance
implementation.uses = deferral.sigUses
// What will we do with the return type info in here? // What will we do with the return type info in here?
return implementation return implementation
} }
@ -1467,6 +1531,9 @@ 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 imps[aSignature].fromInstance = deferral.fromInstance
imps[aSignature].fromBehavior = deferral.fromBehavior
imps[aSignature].instance = deferral.instance
imps[aSignature].uses = deferral.sigUses
} }
} }
} }
@ -1576,7 +1643,8 @@ export default class PocomathInstance {
return wantsType return wantsType
}) })
_findSubtypeImpl(name, imps, neededSig) { _findSubtypeImpl(name, imps, neededSig, raw = false) {
const detemplate = !raw
if (neededSig in imps) return neededSig if (neededSig in imps) return neededSig
let foundSig = false let foundSig = false
const typeList = typeListOfSignature(neededSig) const typeList = typeListOfSignature(neededSig)
@ -1589,16 +1657,16 @@ export default class PocomathInstance {
let myType = typeList[k] let myType = typeList[k]
let otherType = otherTypeList[k] let otherType = otherTypeList[k]
if (otherType === theTemplateParam) { if (otherType === theTemplateParam) {
otherTypeList[k] = paramBound if (detemplate) otherTypeList[k] = paramBound
otherType = paramBound otherType = paramBound
} }
if (otherType === restTemplateParam) { if (otherType === restTemplateParam) {
otherTypeList[k] = `...${paramBound}` if (detemplate) otherTypeList[k] = `...${paramBound}`
otherType = paramBound otherType = paramBound
} }
const adjustedOtherType = otherType.replaceAll(templateCall, '') const adjustedOtherType = otherType.replaceAll(templateCall, '')
if (adjustedOtherType !== otherType) { if (adjustedOtherType !== otherType) {
otherTypeList[k] = adjustedOtherType if (detemplate) otherTypeList[k] = adjustedOtherType
otherType = adjustedOtherType otherType = adjustedOtherType
} }
if (myType.slice(0,3) === '...') myType = myType.slice(3) if (myType.slice(0,3) === '...') myType = myType.slice(3)
@ -1607,9 +1675,11 @@ export default class PocomathInstance {
if (otherBound) { if (otherBound) {
paramBound = otherBound[2] paramBound = otherBound[2]
otherType = paramBound otherType = paramBound
if (detemplate) {
otherTypeList[k] = otherBound[1].replaceAll( otherTypeList[k] = otherBound[1].replaceAll(
theTemplateParam, paramBound) theTemplateParam, paramBound)
} }
}
if (otherType === 'any') continue if (otherType === 'any') continue
if (otherType === UniversalType) continue if (otherType === UniversalType) continue
if (myType === otherType) continue if (myType === otherType) continue
@ -1619,7 +1689,7 @@ export default class PocomathInstance {
if (this.instantiateTemplate(otherType, myType)) { if (this.instantiateTemplate(otherType, myType)) {
let dummy let dummy
dummy = this[name] // for side effects dummy = this[name] // for side effects
return this._findSubtypeImpl(name, this._imps[name], neededSig) return this._findSubtypeImpl(name, this._imps[name], neededSig, raw)
} }
} }
if (!(otherType in this.Types)) { if (!(otherType in this.Types)) {
@ -1658,6 +1728,10 @@ export default class PocomathInstance {
of typedFunction._typedFunctionData.signatureMap) { of typedFunction._typedFunctionData.signatureMap) {
let allMatched = true let allMatched = true
const implTypes = typeListOfSignature(implSig) const implTypes = typeListOfSignature(implSig)
if (implTypes.length > wantTypes.length) {
// Not enough arguments for that implementation
continue
}
for (let i = 0; i < wantTypes.length; ++i) { for (let i = 0; i < wantTypes.length; ++i) {
const implIndex = Math.min(i, implTypes.length - 1) const implIndex = Math.min(i, implTypes.length - 1)
let implType = implTypes[implIndex] let implType = implTypes[implIndex]
@ -1682,7 +1756,7 @@ export default class PocomathInstance {
} }
} }
if (!(this._imps[name])) return undefined if (!(this._imps[name])) return undefined
const foundsig = this._findSubtypeImpl(name, this._imps[name], sig) const foundsig = this._findSubtypeImpl(name, this._imps[name], sig, 'raw')
if (foundsig) { if (foundsig) {
if (haveTF) { if (haveTF) {
try { try {
@ -1690,19 +1764,74 @@ export default class PocomathInstance {
} catch { } catch {
} }
} }
const instantiationMatcher =
'^'
+ substituteInSignature(foundsig, theTemplateParam, '(.*)')
.replaceAll(UniversalType, '(.*)')
+ '$'
const instanceMatch = sig.match(instantiationMatcher)
let possibleInstantiator = false
if (instanceMatch) {
possibleInstantiator = instanceMatch[1]
for (let i = 2; i < instanceMatch.length; ++i) {
if (possibleInstantiator !== instanceMatch[i]) {
possibleInstantiator = false
break
}
}
}
if (possibleInstantiator) {
const behavior = this._imps[name][foundsig]
let newInstance
if (behavior) {
if (!(possibleInstantiator in behavior.hasInstantiations)) {
newInstance = this._instantiateTemplateImplementation(
name, foundsig, possibleInstantiator)
} else {
// OK, so we actually have the instantiation. Let's get it
newInstance = this._TFimps[name][sig]
}
// But we may not have taken advantage of conversions
this._invalidate(name)
const tryAgain = this[name]
let betterInstance
if (this._typed.isTypedFunction(tryAgain)) {
betterInstance = this._typed.findSignature(tryAgain, sig)
}
if (betterInstance) {
newInstance = betterInstance
} else {
newInstance = {
fn: newInstance,
implementation: newInstance
}
}
if (newInstance) return newInstance
}
}
const catchallSig = this._findSubtypeImpl(name, this._imps[name], sig)
if (catchallSig !== foundsig) {
try { try {
return this._metaTyped.findSignature(this._meta[name], foundsig) return this._metaTyped.findSignature(
this._meta[name], catchallSig)
} catch { } catch {
} }
}
// We have an implementation but not a typed function. Do the best // We have an implementation but not a typed function. Do the best
// we can: // we can:
const foundImpl = this._imps[name][foundsig] const restoredSig = foundsig.replaceAll('ground', theTemplateParam)
const foundImpl = this._imps[name][restoredSig]
const needs = {} const needs = {}
for (const dep of foundImpl.uses) { for (const dep of foundImpl.uses) {
const [base, sig] = dep.split('()') const [base, sig] = dep.split(/[()]/)
if (sig) {
needs[dep] = this.resolve(base, sig) needs[dep] = this.resolve(base, sig)
} else {
needs[dep] = this[dep]
}
} }
const pseudoImpl = foundImpl.does(needs) const pseudoImpl = foundImpl.does(needs)
pseudoImpl.fromInstance = this
return {fn: pseudoImpl, implementation: pseudoImpl} return {fn: pseudoImpl, implementation: pseudoImpl}
} }
// Hmm, no luck. Make sure bundle is up-to-date and retry: // Hmm, no luck. Make sure bundle is up-to-date and retry:

View File

@ -6,7 +6,7 @@ export function dependencyExtractor(destinationSet) {
return new Proxy({}, { return new Proxy({}, {
get: (target, property) => { get: (target, property) => {
destinationSet.add(property) destinationSet.add(property)
return {} return {checkingDependency: true}
} }
}) })
} }

View File

@ -4,5 +4,5 @@ export * from './Types/number.mjs'
export const add = { export const add = {
// Note the below assumes that all subtypes of number that will be defined // Note the below assumes that all subtypes of number that will be defined
// are closed under addition! // are closed under addition!
'T:number, T': ({T}) => Returns(T, (m,n) => m+n) 'T:number,T': ({T}) => Returns(T, (m,n) => m+n)
} }

View File

@ -0,0 +1,34 @@
import assert from 'assert'
import math from '../../src/pocomath.mjs'
describe('polynomialRoot', () => {
it('should solve a linear equation with real coefficients', function () {
assert.deepEqual(math.polynomialRoot(6, 3), math.tuple(-2))
assert.deepEqual(
math.polynomialRoot(math.complex(-3, 2), 2),
math.tuple(math.complex(1.5, -1)))
assert.deepEqual(
math.polynomialRoot(math.complex(3, 1), math.complex(-1, -1)),
math.tuple(math.complex(2, -1)))
})
// Should be safe now to capture the functions:
const complex = math.complex
const pRoot = math.polynomialRoot
const tup = math.tuple
it('should solve a quadratic equation with a double root', function () {
assert.deepEqual(pRoot(4, 4, 1), tup(-2))
assert.deepEqual(
pRoot(complex(0, 2), complex(2, 2), 1), tup(complex(-1, -1)))
})
it('should solve a quadratic with two distinct roots', function () {
assert.deepEqual(pRoot(-3, 2, 1), tup(1, -3))
assert.deepEqual(pRoot(-2, 0, 1), tup(math.sqrt(2), -math.sqrt(2)))
assert.deepEqual(
pRoot(4, 2, 1),
tup(complex(-1, math.sqrt(3)), complex(-1, -math.sqrt(3))))
assert.deepEqual(
pRoot(complex(3, 1), -3, 1), tup(complex(1, 1), complex(2, -1)))
})
})