feat: add Vector generic type

This commit is contained in:
Glen Whitney 2025-04-28 14:29:08 -07:00
parent 0765ba7202
commit cc4d77f128
4 changed files with 85 additions and 4 deletions

View file

@ -1,8 +1,9 @@
import * as booleans from './boolean/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 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'
// 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.)
// Hence, in building the math instance, we put generics first because
// 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)
// 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

43
src/vector/Vector.js Normal file
View 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)
}
})

View 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
View file

@ -0,0 +1 @@
export * as typeDefinition from './Vector.js'