feat: add Vector generic type
This commit is contained in:
parent
0765ba7202
commit
cc4d77f128
4 changed files with 85 additions and 4 deletions
|
@ -1,8 +1,9 @@
|
||||||
import * as booleans from './boolean/all.js'
|
|
||||||
import * as coretypes from './coretypes/all.js'
|
import * as coretypes from './coretypes/all.js'
|
||||||
|
import * as booleans from './boolean/all.js'
|
||||||
|
import * as complex from './complex/all.js'
|
||||||
import * as generics from './generic/all.js'
|
import * as generics from './generic/all.js'
|
||||||
import * as numbers from './number/all.js'
|
import * as numbers from './number/all.js'
|
||||||
import * as complex from './complex/all.js'
|
import * as vectors from './vector/all.js'
|
||||||
import {TypeDispatcher} from '#core/TypeDispatcher.js'
|
import {TypeDispatcher} from '#core/TypeDispatcher.js'
|
||||||
|
|
||||||
// At the moment, since we are not sorting patterns in any way,
|
// At the moment, since we are not sorting patterns in any way,
|
||||||
|
@ -12,9 +13,10 @@ import {TypeDispatcher} from '#core/TypeDispatcher.js'
|
||||||
// whatever has been merged before.)
|
// whatever has been merged before.)
|
||||||
// Hence, in building the math instance, we put generics first because
|
// Hence, in building the math instance, we put generics first because
|
||||||
// they should only kick in when there are not specific implementations,
|
// they should only kick in when there are not specific implementations,
|
||||||
// and complex next becausewe want its conversion (which converts _any_
|
// and complex next because we want its conversion (which converts _any_
|
||||||
// non-complex type to complex, potentially making a poor overload choice)
|
// non-complex type to complex, potentially making a poor overload choice)
|
||||||
// to be tried last.
|
// to be tried last.
|
||||||
const math = new TypeDispatcher(generics, complex, booleans, coretypes, numbers)
|
const math = new TypeDispatcher(
|
||||||
|
generics, complex, vectors, booleans, coretypes, numbers)
|
||||||
|
|
||||||
export default math
|
export default math
|
||||||
|
|
43
src/vector/Vector.js
Normal file
43
src/vector/Vector.js
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import {NotAType, Type} from '#core/Type.js'
|
||||||
|
|
||||||
|
const isVector = v => Array.isArray(v)
|
||||||
|
|
||||||
|
export const Vector = new Type(isVector, {
|
||||||
|
specialize(CompType) {
|
||||||
|
const compTest = CompType.test
|
||||||
|
const specTest = v => isVector(v) && v.every(compTest)
|
||||||
|
const typeName = `Vector(${CompType})`
|
||||||
|
if (CompType.concrete) {
|
||||||
|
const typeOptions = {typeName}
|
||||||
|
if ('zero' in CompType) typeOptions.zero = [CompType.zero]
|
||||||
|
const vectorCompType = new Type(specTest, typeOptions)
|
||||||
|
vectorCompType.Component = CompType
|
||||||
|
vectorCompType.vector = true
|
||||||
|
return vectorCompType
|
||||||
|
}
|
||||||
|
// Wrapping a generic type in Vector
|
||||||
|
return new Type(specTest, {
|
||||||
|
typeName,
|
||||||
|
specialize: (...args) => this.specialize(CompType.specialize(...args)),
|
||||||
|
specializesTo: VT => this.specializesTo(VT)
|
||||||
|
&& CompType.specializesTo(VT.Component),
|
||||||
|
refine: (v, typer) => {
|
||||||
|
const eltTypes = v.map(elt => CompType.refine(elt, typer))
|
||||||
|
const newCompType = eltTypes[0]
|
||||||
|
if (eltTypes.some(T => T !== newCompType)) {
|
||||||
|
throw new TypeError(
|
||||||
|
`can't refine ${typeName} to ${v}; `
|
||||||
|
+ `not all entries have type ${newCompType}`)
|
||||||
|
}
|
||||||
|
return this.specialize(newCompType)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
specializesTo: VT => VT.vector,
|
||||||
|
refine(v, typer) {
|
||||||
|
const eltTypes = v.map(elt => typer(elt))
|
||||||
|
let compType = eltTypes[0]
|
||||||
|
if (eltTypes.some(T => T !== compType)) compType = NotAType
|
||||||
|
return this.specialize(compType)
|
||||||
|
}
|
||||||
|
})
|
35
src/vector/__test__/Vector.spec.js
Normal file
35
src/vector/__test__/Vector.spec.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import assert from 'assert'
|
||||||
|
import {Vector} from '../Vector.js'
|
||||||
|
import {NotAType} from '#core/Type.js'
|
||||||
|
import math from '#nanomath'
|
||||||
|
|
||||||
|
describe('Vector Type', () => {
|
||||||
|
it('correctly recognizes vectors', () => {
|
||||||
|
assert(Vector.test([3, 4]))
|
||||||
|
assert(!Vector.test({re: 3, im: 4}))
|
||||||
|
})
|
||||||
|
it('can fully type vectors', () => {
|
||||||
|
const {BooleanT, Complex, NumberT} = math.types
|
||||||
|
const typ = math.typeOf
|
||||||
|
const cplx = math.complex
|
||||||
|
assert.strictEqual(typ([3, 4]), Vector(NumberT))
|
||||||
|
assert.strictEqual(typ([true, false]), Vector(BooleanT))
|
||||||
|
assert.strictEqual(typ([3, false]), Vector(NotAType))
|
||||||
|
assert.strictEqual(typ([cplx(3, 4), cplx(5)]), Vector(Complex(NumberT)))
|
||||||
|
assert.strictEqual(typ([[3, 4], [5]]), Vector(Vector(NumberT)))
|
||||||
|
})
|
||||||
|
it('can refine when nested', () => {
|
||||||
|
const {Complex, NumberT} = math.types
|
||||||
|
const cplx = math.complex
|
||||||
|
const VecComplex = Vector(Complex)
|
||||||
|
const vcEx = [cplx(3, 4), cplx(5)]
|
||||||
|
assert(VecComplex.test(vcEx))
|
||||||
|
const vcType = VecComplex.refine(vcEx, math.typeOf)
|
||||||
|
assert.strictEqual(vcType, Vector(Complex(NumberT)))
|
||||||
|
const CplxVec = Complex(Vector)
|
||||||
|
const cvEx = cplx([3,4], [5, 0])
|
||||||
|
assert(CplxVec.test(cvEx))
|
||||||
|
const cvType = CplxVec.refine(cvEx, math.typeOf)
|
||||||
|
assert.strictEqual(cvType, Complex(Vector(NumberT)))
|
||||||
|
})
|
||||||
|
})
|
1
src/vector/all.js
Normal file
1
src/vector/all.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export * as typeDefinition from './Vector.js'
|
Loading…
Add table
Add a link
Reference in a new issue