Glen Whitney
31add66f4c
Provides the infrastructure to allow annotating the return types of functions, and does so for essentially every operation in the system (the only known exceptions being add, multiply, etc., on arbitrarily many arguments). One main infrastructure enhancements are bounded template types, e.g. `T:number` being a template parameter where T can take on the type `number` or any subtype thereof. A main internal enhancement is that base template types are no longer added to the typed universe; rather, there is a secondary, "meta" typed universe where they live. The primary point/purpose of this change is then the necessary search order for implementations can be much better modeled by typed-function's search order, using the `onMismatch` facility to redirect the search from fully instantiated implementations to the generic catchall implementations for each template (these catchalls live in the meta universe). Numerous other small improvements and bugfixes were encountered along the way. Co-authored-by: Glen Whitney <glen@studioinfinity.org> Reviewed-on: #53
144 lines
6.0 KiB
JavaScript
144 lines
6.0 KiB
JavaScript
import assert from 'assert'
|
|
import math from '../src/pocomath.mjs'
|
|
import PocomathInstance from '../src/core/PocomathInstance.mjs'
|
|
import * as numbers from '../src/number/all.mjs'
|
|
import * as numberAdd from '../src/number/add.mjs'
|
|
import * as numberZero from '../src/number/zero.mjs'
|
|
import {add as genericAdd} from '../src/generic/arithmetic.mjs'
|
|
import * as complex from '../src/complex/all.mjs'
|
|
import * as complexAdd from '../src/complex/add.mjs'
|
|
import * as complexNegate from '../src/complex/negate.mjs'
|
|
import * as complexComplex from '../src/complex/complex.mjs'
|
|
import * as bigintAdd from '../src/bigint/add.mjs'
|
|
import * as bigintZero from '../src/bigint/zero.mjs'
|
|
import * as concreteSubtract from '../src/generic/subtract.concrete.mjs'
|
|
import * as genericSubtract from '../src/generic/subtract.mjs'
|
|
import extendToComplex from '../src/complex/extendToComplex.mjs'
|
|
|
|
const bw = new PocomathInstance('backwards')
|
|
describe('A custom instance', () => {
|
|
it("works when partially assembled", () => {
|
|
bw.install(complex)
|
|
// Not much we can call without any number types:
|
|
assert.deepStrictEqual(bw.complex(undefined, undefined), undefined)
|
|
assert.deepStrictEqual(
|
|
bw.chain(undefined).complex(undefined).value,
|
|
undefined)
|
|
// Don't have a way to negate things, for example:
|
|
assert.throws(() => bw.negate(2), TypeError)
|
|
})
|
|
|
|
it("can be assembled in any order", () => {
|
|
bw.install(numbers)
|
|
bw.installType('string', {test: s => typeof s === 'string'})
|
|
assert.strictEqual(bw.subtract(16, bw.add(3,4,2)), 7)
|
|
assert.strictEqual(bw.negate('8'), -8)
|
|
assert.deepStrictEqual(bw.add(bw.complex(1,3), 1), {re: 2, im: 3})
|
|
assert.deepStrictEqual(
|
|
bw.subtract(16, bw.add(3, bw.complex(0,4), 2)),
|
|
math.complex(11, -4)) // note both instances coexist
|
|
assert.deepStrictEqual(bw.negate(bw.complex(3, '8')).im, -8)
|
|
})
|
|
|
|
it("can be assembled piecemeal", () => {
|
|
const pm = new PocomathInstance('piecemeal')
|
|
pm.install(numbers)
|
|
assert.strictEqual(pm.subtract(5, 10), -5)
|
|
assert.strictEqual(pm.floor(3.7), 3)
|
|
assert.throws(() => pm.floor(10n), TypeError)
|
|
assert.strictEqual(pm.chain(5).add(7).value, 12)
|
|
pm.install(complexAdd)
|
|
pm.install(complexNegate)
|
|
pm.install(complexComplex)
|
|
// Should be enough to allow complex subtraction, as subtract is generic:
|
|
assert.deepStrictEqual(
|
|
pm.subtract(pm.complex(5, 0), pm.complex(10, 1)),
|
|
math.complex(-5, -1))
|
|
// And now floor has been activated for Complex as well, since the type
|
|
// is present
|
|
const fracComplex = math.complex(1.9, 0)
|
|
const intComplex = math.complex(1)
|
|
assert.deepStrictEqual(pm.floor(fracComplex), intComplex)
|
|
// And the chain functions refresh themselves:
|
|
assert.deepStrictEqual(
|
|
pm.chain(5).add(pm.chain(0).complex(7).value).value, math.complex(5,7))
|
|
})
|
|
|
|
it("can defer definition of (even used) types", () => {
|
|
const dt = new PocomathInstance('Deferred Types')
|
|
dt.install(numberAdd)
|
|
dt.install(numberZero) // for promoting numbers to complex, to fill in im
|
|
dt.install({times: {
|
|
'number,number': () => (m,n) => m*n,
|
|
'Complex<T>,Complex<T>': ({'complex(T,T)': cplx}) => (w,z) => {
|
|
return cplx(w.re*z.re - w.im*z.im, w.re*z.im + w.im*z.re)
|
|
}
|
|
}})
|
|
// complex type not present but should still be able to add numbers:
|
|
assert.strictEqual(dt.times(3,5), 15)
|
|
dt.install(complexComplex)
|
|
// times should now rebundle to allow complex:
|
|
assert.deepStrictEqual(
|
|
dt.times(dt.complex(2,3), dt.complex(2,-3)), dt.complex(13))
|
|
})
|
|
|
|
it("can selectively import in cute ways", async function () {
|
|
const cherry = new PocomathInstance('cherry')
|
|
cherry.install(numberAdd)
|
|
cherry.install(numberZero) // for complex promotion
|
|
await extendToComplex(cherry)
|
|
cherry.install({add: genericAdd})
|
|
/* Now we have an instance that supports addition for number and complex
|
|
and little else:
|
|
*/
|
|
assert.strictEqual(cherry.add(3, 4, 2), 9)
|
|
assert.deepStrictEqual(
|
|
cherry.add(cherry.complex(3, 3), 4, cherry.complex(2, 2)),
|
|
math.complex(9,5))
|
|
assert.strictEqual('subtract' in cherry, false)
|
|
assert.strictEqual('negate' in cherry, false)
|
|
})
|
|
|
|
it("can use bundles that are closed under dependency", () => {
|
|
const ok = new PocomathInstance('concrete')
|
|
ok.install(concreteSubtract)
|
|
assert.strictEqual(ok.subtract(7, 5), 2)
|
|
})
|
|
|
|
it("can load generics and then import their dependences", async function () {
|
|
const chase = new PocomathInstance('Chase Dependencies')
|
|
chase.install(genericSubtract)
|
|
chase.install(complexComplex) // for convenience to build complex numbers
|
|
await chase.importDependencies(['bigint', 'complex'])
|
|
/* Now we have an instance that supports subtraction for Gaussian
|
|
integers.
|
|
*/
|
|
assert.deepStrictEqual(
|
|
chase.subtract(chase.complex(3n, 2n), chase.complex(2n, 5n)),
|
|
math.complex(1n, -3n))
|
|
})
|
|
|
|
it("instantiates templates correctly", () => {
|
|
const inst = new PocomathInstance('InstantiateTemplates')
|
|
inst.install(numberAdd)
|
|
inst.install({typeMerge: {'T,T': ({T}) => (t,u) => 'Merge to ' + T }})
|
|
assert.strictEqual(inst.typeMerge(7,6.28), 'Merge to number')
|
|
assert.strictEqual(inst.typeMerge(7,6), 'Merge to NumInt')
|
|
assert.strictEqual(inst.typeMerge(7.35,6), 'Merge to number')
|
|
inst.install(complexAdd)
|
|
inst.install(complexComplex)
|
|
inst.install(bigintAdd)
|
|
inst.install(bigintZero) // for complex promotion
|
|
assert.strictEqual(
|
|
inst.typeMerge(6n, inst.complex(3n, 2n)),
|
|
'Merge to Complex<bigint>')
|
|
assert.strictEqual(
|
|
inst.typeMerge(3, inst.complex(4.5,2.1)),
|
|
'Merge to Complex<number>')
|
|
assert.throws(
|
|
() => inst.typeMerge(3, inst.complex(3n)), TypeError)
|
|
inst.install(genericSubtract)
|
|
assert.throws(() => inst.typeMerge(3, undefined), TypeError)
|
|
})
|
|
})
|