feat: methods on Complex #24
33 changed files with 428 additions and 106 deletions
|
@ -1,6 +1,7 @@
|
||||||
import {Type} from '#core/Type.js'
|
import {Type} from '#core/Type.js'
|
||||||
|
|
||||||
export const BooleanT = new Type(n => typeof n === 'boolean', {
|
export const BooleanT = new Type(n => typeof n === 'boolean', {
|
||||||
|
typeName: 'BooleanT',
|
||||||
zero: false,
|
zero: false,
|
||||||
one: true
|
one: true
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {BooleanT} from './BooleanT.js'
|
import {BooleanT} from './BooleanT.js'
|
||||||
import {match} from '#core/helpers.js'
|
import {match} from '#core/TypePatterns.js'
|
||||||
import {Returns, Type, TypeOfTypes, Undefined} from '#core/Type.js'
|
import {Returns, Type, TypeOfTypes, Undefined} from '#core/Type.js'
|
||||||
import {NumberT} from '#number/NumberT.js'
|
import {NumberT} from '#number/NumberT.js'
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {Type} from '#core/Type.js'
|
import {Returns, Type} from '#core/Type.js'
|
||||||
import {match} from '#core/helpers.js'
|
import {match} from '#core/TypePatterns.js'
|
||||||
|
|
||||||
const isComplex = z => z && typeof z === 'object' && 're' in z && 'im' in z
|
const isComplex = z => z && typeof z === 'object' && 're' in z && 'im' in z
|
||||||
|
|
||||||
|
@ -66,6 +66,12 @@ function complexSpecialize(ComponentType) {
|
||||||
export const Complex = new Type(isComplex, {
|
export const Complex = new Type(isComplex, {
|
||||||
specialize: complexSpecialize,
|
specialize: complexSpecialize,
|
||||||
specializesTo,
|
specializesTo,
|
||||||
|
from: [match( // can promote any non-complex type T to Complex(T) as needed
|
||||||
|
// but watch out, this should be tried late, because it can preclude
|
||||||
|
// other more reasonable conversions like bool => number.
|
||||||
|
T => !T.complex,
|
||||||
|
(math, T) => Returns(Complex(T), r => math.complex(r, math.zero(T)))
|
||||||
|
)],
|
||||||
refine: function(z, typer) {
|
refine: function(z, typer) {
|
||||||
const reType = typer(z.re)
|
const reType = typer(z.re)
|
||||||
const imType = typer(z.im)
|
const imType = typer(z.im)
|
||||||
|
|
78
src/complex/__test__/arithmetic.spec.js
Normal file
78
src/complex/__test__/arithmetic.spec.js
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import assert from 'assert'
|
||||||
|
import math from '#nanomath'
|
||||||
|
import {Complex} from '../Complex.js'
|
||||||
|
import {NumberT} from '#number/NumberT.js'
|
||||||
|
|
||||||
|
const cplx = math.complex
|
||||||
|
|
||||||
|
describe('complex arithmetic operations', () => {
|
||||||
|
it('computes absquare of complex numbers', () => {
|
||||||
|
assert.strictEqual(math.absquare(cplx(3, 4)), 25)
|
||||||
|
assert.strictEqual(math.absquare(cplx(cplx(2, 3), cplx(4,5))), 54)
|
||||||
|
assert.strictEqual(math.absquare(cplx(true, true)), 2)
|
||||||
|
})
|
||||||
|
it('adds complex numbers', () => {
|
||||||
|
const z = cplx(3, 4)
|
||||||
|
const add = math.add
|
||||||
|
assert.deepStrictEqual(add(z, cplx(-1, 1)), cplx(2, 5))
|
||||||
|
assert.deepStrictEqual(add(z, cplx(true, false)), cplx(4, 4))
|
||||||
|
assert.deepStrictEqual(add(cplx(false, true), z), cplx(3, 5))
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
add(cplx(z, z), cplx(cplx(0.5, 0.5), z)),
|
||||||
|
cplx(cplx(3.5, 4.5), cplx(6, 8)))
|
||||||
|
assert.deepStrictEqual(add(z, 5), cplx(8, 4))
|
||||||
|
assert.deepStrictEqual(add(true, z), cplx(4, 4))
|
||||||
|
assert.deepStrictEqual(add(cplx(z,z), 10), cplx(cplx(13, 4), z))
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
add(cplx(z,z), cplx(10,20)), cplx(cplx(13, 4), cplx(23, 4)))
|
||||||
|
})
|
||||||
|
it('conjugates complex numbers', () => {
|
||||||
|
const conj = math.conj
|
||||||
|
const z = cplx(3, 4)
|
||||||
|
assert.deepStrictEqual(conj(z), cplx(3, -4))
|
||||||
|
assert.deepStrictEqual(conj(cplx(z,z)), cplx(cplx(3, -4), cplx(-3, -4)))
|
||||||
|
})
|
||||||
|
it('divides complex numbers', () => {
|
||||||
|
const div = math.divide
|
||||||
|
const z = cplx(3, 4)
|
||||||
|
assert.deepStrictEqual(div(z, cplx(0, 1)), cplx(4, -3))
|
||||||
|
assert(math.equal(div(z, z), 1))
|
||||||
|
// should probably have a quaternion example, but it's intricate
|
||||||
|
})
|
||||||
|
it('inverts complex numbers', () => {
|
||||||
|
const inv = math.invert
|
||||||
|
assert.deepStrictEqual(inv(cplx(0, 1)), cplx(0, -1))
|
||||||
|
assert.deepStrictEqual(inv(cplx(3, 4)), cplx(3/25, -4/25))
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
inv(cplx(cplx(1, 2), cplx(4, 2))),
|
||||||
|
cplx(cplx(1/25, -2/25), cplx(-4/25, -2/25)))
|
||||||
|
})
|
||||||
|
it('multiplies complex numbers', () => {
|
||||||
|
const mult = math.multiply
|
||||||
|
const z = cplx(3, 4)
|
||||||
|
assert.deepStrictEqual(mult(z, z), cplx(-7, 24))
|
||||||
|
assert(math.equal(mult(z, math.conj(z)), 25))
|
||||||
|
const q0 = cplx(cplx(1, 1), math.zero(Complex(NumberT)))
|
||||||
|
const q1 = cplx(cplx(1, 0.5), cplx(0.5, 0.75))
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
mult(q0, q1), cplx(cplx(0.5, 1.5), cplx(1.25, 0.25)))
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
mult(q0, cplx(cplx(2, 0.1), cplx(1, 0.1))),
|
||||||
|
cplx(cplx(1.9, 2.1), cplx(1.1, -0.9)))
|
||||||
|
})
|
||||||
|
it('subtracts complex numbers', () => {
|
||||||
|
const z = cplx(3, 4)
|
||||||
|
const sub = math.subtract
|
||||||
|
assert.deepStrictEqual(sub(z, cplx(-1, 1)), cplx(4, 3))
|
||||||
|
assert.deepStrictEqual(sub(z, cplx(true, false)), cplx(2, 4))
|
||||||
|
assert.deepStrictEqual(sub(cplx(false, true), z), cplx(-3, -3))
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
sub(cplx(z, z), cplx(cplx(0.5, 0.5), z)),
|
||||||
|
cplx(cplx(2.5, 3.5), cplx(0, 0)))
|
||||||
|
assert.deepStrictEqual(sub(z, 5), cplx(-2, 4))
|
||||||
|
assert.deepStrictEqual(sub(true, z), cplx(-2, -4))
|
||||||
|
assert.deepStrictEqual(sub(cplx(z,z), 10), cplx(cplx(-7, 4), z))
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
sub(cplx(z,z), cplx(10,20)), cplx(cplx(-7, 4), cplx(-17, 4)))
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,12 +0,0 @@
|
||||||
import assert from 'assert'
|
|
||||||
import math from '#nanomath'
|
|
||||||
|
|
||||||
describe('complex type operations', () => {
|
|
||||||
it('converts to number', () => {
|
|
||||||
assert.deepStrictEqual(math.complex(3), {re: 3, im: 0})
|
|
||||||
assert.deepStrictEqual(math.complex(NaN), {re: NaN, im: NaN})
|
|
||||||
assert.deepStrictEqual(math.complex(3, -1), {re: 3, im: -1})
|
|
||||||
assert.deepStrictEqual(math.complex(false, true), {re: false, im: true})
|
|
||||||
assert.throws(() => math.complex(3, false), RangeError)
|
|
||||||
})
|
|
||||||
})
|
|
39
src/complex/__test__/type.spec.js
Normal file
39
src/complex/__test__/type.spec.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import assert from 'assert'
|
||||||
|
import math from '#nanomath'
|
||||||
|
|
||||||
|
const cplx = math.complex
|
||||||
|
|
||||||
|
describe('complex type operations', () => {
|
||||||
|
it('converts to number', () => {
|
||||||
|
assert.deepStrictEqual(cplx(3), {re: 3, im: 0})
|
||||||
|
assert.deepStrictEqual(cplx(NaN), {re: NaN, im: NaN})
|
||||||
|
assert.deepStrictEqual(cplx(3, -1), {re: 3, im: -1})
|
||||||
|
assert.deepStrictEqual(cplx(false, true), {re: false, im: true})
|
||||||
|
assert.throws(() => cplx(3, false), RangeError)
|
||||||
|
})
|
||||||
|
it('calculates the argument of a complex number', () => {
|
||||||
|
assert.strictEqual(math.arg(cplx(1, Math.sqrt(3))), Math.PI/3)
|
||||||
|
assert.strictEqual(math.arg(cplx(true, true)), Math.PI/4)
|
||||||
|
})
|
||||||
|
it('detects associates of a complex number', () => {
|
||||||
|
const z = cplx(3, 4)
|
||||||
|
const assoc = math.associate
|
||||||
|
assert(assoc(z, z))
|
||||||
|
assert(assoc(z, cplx(-3, -4)))
|
||||||
|
assert(assoc(z, cplx(-4, 3)))
|
||||||
|
assert(assoc(z, cplx(4, -3)))
|
||||||
|
assert(!assoc(z, math.conj(z)))
|
||||||
|
const b = cplx(true, true)
|
||||||
|
assert(assoc(b, cplx(-1, 1)))
|
||||||
|
assert(assoc(cplx(1, 1), b))
|
||||||
|
assert(assoc(cplx(-1, -1), b))
|
||||||
|
assert(assoc(cplx(1, -1), b))
|
||||||
|
assert(assoc(cplx(-1, 1), b))
|
||||||
|
assert(!assoc(b, cplx(false, true)))
|
||||||
|
assert(!assoc(cplx(0, 1), b))
|
||||||
|
})
|
||||||
|
it('computes cis of an angle', () => {
|
||||||
|
assert(math.equal(math.cis(0), 1))
|
||||||
|
assert(math.equal(math.cis(Math.PI/3), cplx(0.5, Math.sqrt(3)/2)))
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,2 +1,5 @@
|
||||||
export * as typeDefinition from './Complex.js'
|
export * as typeDefinition from './Complex.js'
|
||||||
|
export * as arithmetic from './arithmetic.js'
|
||||||
|
export * as relational from './relational.js'
|
||||||
export * as type from './type.js'
|
export * as type from './type.js'
|
||||||
|
export * as utilities from './utils.js'
|
||||||
|
|
69
src/complex/arithmetic.js
Normal file
69
src/complex/arithmetic.js
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
import {Complex} from './Complex.js'
|
||||||
|
import {promoteBinary, promoteUnary} from './helpers.js'
|
||||||
|
import {ResolutionError} from '#core/helpers.js'
|
||||||
|
import {match} from '#core/TypePatterns.js'
|
||||||
|
import {ReturnsAs} from '#generic/helpers.js'
|
||||||
|
|
||||||
|
export const absquare = match(Complex, (math, C) => {
|
||||||
|
const compAbsq = math.absquare.resolve([C.Component])
|
||||||
|
const R = compAbsq.returns
|
||||||
|
const add = math.add.resolve([R,R])
|
||||||
|
return ReturnsAs(add, z => add(compAbsq(z.re), compAbsq(z.im)))
|
||||||
|
})
|
||||||
|
|
||||||
|
export const add = promoteBinary('add')
|
||||||
|
|
||||||
|
export const conj = match(Complex, (math, C) => {
|
||||||
|
const neg = math.negate.resolve(C.Component)
|
||||||
|
const compConj = math.conj.resolve(C.Component)
|
||||||
|
const cplx = math.complex.resolve([compConj.returns, neg.returns])
|
||||||
|
return ReturnsAs(cplx, z => cplx(compConj(z.re), neg(z.im)))
|
||||||
|
})
|
||||||
|
|
||||||
|
export const divide = [
|
||||||
|
match([Complex, T => !T.complex], (math, [C, R]) => {
|
||||||
|
const div = math.divide.resolve([C.Component, R])
|
||||||
|
const cplx = math.complex.resolve([div.returns, div.returns])
|
||||||
|
return ReturnsAs(cplx, (z, r) => cplx(div(z.re, r), div(z.im, r)))
|
||||||
|
}),
|
||||||
|
match([Complex, Complex], (math, [W, Z]) => {
|
||||||
|
const inv = math.invert.resolve(Z)
|
||||||
|
const mult = math.multiply.resolve([W, inv.returns])
|
||||||
|
return ReturnsAs(mult, (w, z) => mult(w, inv(z)))
|
||||||
|
})
|
||||||
|
]
|
||||||
|
|
||||||
|
export const invert = match(Complex, (math, C) => {
|
||||||
|
const conj = math.conj.resolve(C)
|
||||||
|
const norm = math.absquare.resolve(C)
|
||||||
|
const div = math.divide.resolve([C.Component, norm.returns])
|
||||||
|
const cplx = math.complex.resolve([div.returns, div.returns])
|
||||||
|
return ReturnsAs(cplx, z => {
|
||||||
|
const c = conj(z)
|
||||||
|
const d = norm(z)
|
||||||
|
return cplx(div(c.re, d), div(c.im, d))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// We want this to work for complex numbers, quaternions, octonions, etc
|
||||||
|
// See https://math.ucr.edu/home/baez/octonions/node5.html
|
||||||
|
export const multiply = match([Complex, Complex], (math, [W, Z]) => {
|
||||||
|
const conj = math.conj.resolve(W.Component)
|
||||||
|
if (conj.returns !== W.Component) {
|
||||||
|
throw new ResolutionError(
|
||||||
|
`conjugation on ${W.Component} returns other type (${conj.returns})`)
|
||||||
|
}
|
||||||
|
const mWZ = math.multiply.resolve([W.Component, Z.Component])
|
||||||
|
const mZW = math.multiply.resolve([Z.Component, W.Component])
|
||||||
|
const sub = math.subtract.resolve([mWZ.returns, mZW.returns])
|
||||||
|
const add = math.add.resolve([mWZ.returns, mZW.returns])
|
||||||
|
const cplx = math.complex.resolve([sub.returns, add.returns])
|
||||||
|
return ReturnsAs(cplx, (w, z) => {
|
||||||
|
const real = sub(mWZ( w.re, z.re), mZW(z.im, conj(w.im)))
|
||||||
|
const imag = add(mWZ(conj(w.re), z.im), mZW(z.re, w.im))
|
||||||
|
return cplx(real, imag)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
export const negate = promoteUnary('negate')
|
||||||
|
export const subtract = promoteBinary('subtract')
|
20
src/complex/helpers.js
Normal file
20
src/complex/helpers.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import {Complex} from './Complex.js'
|
||||||
|
import {match} from '#core/TypePatterns.js'
|
||||||
|
import {ReturnsAs} from '#generic/helpers.js'
|
||||||
|
|
||||||
|
export const promoteUnary = name => match(
|
||||||
|
Complex,
|
||||||
|
(math, C) => {
|
||||||
|
const compOp = math.resolve(name, C.Component)
|
||||||
|
const cplx = math.complex.resolve([compOp.returns, compOp.returns])
|
||||||
|
return ReturnsAs(cplx, z => cplx(compOp(z.re), compOp(z.im)))
|
||||||
|
})
|
||||||
|
|
||||||
|
export const promoteBinary = name => match(
|
||||||
|
[Complex, Complex],
|
||||||
|
(math, [W, Z]) => {
|
||||||
|
const compOp = math.resolve(name, [W.Component, Z.Component])
|
||||||
|
const cplx = math.complex.resolve([compOp.returns, compOp.returns])
|
||||||
|
return ReturnsAs(
|
||||||
|
cplx, (w, z) => cplx(compOp(w.re, z.re), compOp(w.im, z.im)))
|
||||||
|
})
|
24
src/complex/relational.js
Normal file
24
src/complex/relational.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import {Complex} from './Complex.js'
|
||||||
|
import {BooleanT} from '#boolean/BooleanT.js'
|
||||||
|
import {Returns} from '#core/Type.js'
|
||||||
|
import {match, Any, Optional} from '#core/TypePatterns.js'
|
||||||
|
|
||||||
|
export const indistinguishable = match(
|
||||||
|
[Complex, Complex, Optional([Any, Any])],
|
||||||
|
(math, [W, Z, T]) => {
|
||||||
|
let WComp = W.Component
|
||||||
|
let ZComp = Z.Component
|
||||||
|
if (T.length === 0) { // no tolerances
|
||||||
|
const same = math.indistinguishable.resolve([WComp, ZComp])
|
||||||
|
return Returns(
|
||||||
|
BooleanT, (w, z) => same(w.re, z.re) && same(w.im, z.im))
|
||||||
|
}
|
||||||
|
const [[RT, AT]] = T
|
||||||
|
const same = math.indistinguishable.resolve([WComp, ZComp, RT, AT])
|
||||||
|
return Returns(
|
||||||
|
BooleanT,
|
||||||
|
(w, z, [[rT, aT]]) => {
|
||||||
|
return same(w.re, z.re, rT, aT) && same(w.im, z.im, rT, aT)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import {Complex} from './Complex.js'
|
import {Complex} from './Complex.js'
|
||||||
import {match} from "#core/helpers.js"
|
|
||||||
import {Returns} from "#core/Type.js"
|
import {Returns} from "#core/Type.js"
|
||||||
import {Any} from "#core/TypePatterns.js"
|
import {Any, match} from "#core/TypePatterns.js"
|
||||||
|
import {BooleanT} from '#boolean/BooleanT.js'
|
||||||
|
import {NumberT} from '#number/NumberT.js'
|
||||||
|
|
||||||
export const complex = [
|
export const complex = [
|
||||||
match(Any, (math, T) => {
|
match(Any, (math, T) => {
|
||||||
|
@ -25,3 +26,30 @@ export const complex = [
|
||||||
return Returns(Complex(T), (r, m) => ({re: r, im: m}))
|
return Returns(Complex(T), (r, m) => ({re: r, im: m}))
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
|
||||||
|
export const arg = match(
|
||||||
|
Complex(NumberT), Returns(NumberT, z => Math.atan2(z.im, z.re)))
|
||||||
|
|
||||||
|
/* Returns true if w is z multiplied by a complex unit */
|
||||||
|
export const associate = match([Complex, Complex], (math, [W, Z]) => {
|
||||||
|
if (Z.Component.complex) {
|
||||||
|
throw new Error(
|
||||||
|
`The group of units of type ${Z} is not yet implemented`)
|
||||||
|
}
|
||||||
|
const eq = math.equal.resolve([W, Z])
|
||||||
|
const neg = math.negate.resolve(Z)
|
||||||
|
const eqN = math.equal.resolve([W, neg.returns])
|
||||||
|
const mult = math.multiply.resolve([Z, Z])
|
||||||
|
const eqM = math.equal.resolve([W, mult.returns])
|
||||||
|
const negM = math.negate.resolve(mult.returns)
|
||||||
|
const eqNM = math.equal.resolve([W, negM.returns])
|
||||||
|
const iZ = math.complex(math.zero(Z.Component), math.one(Z.Component))
|
||||||
|
return Returns(BooleanT, (w, z) => {
|
||||||
|
if (eq(w, z) || eqN(w, neg(z))) return true
|
||||||
|
const iz = mult(iZ, z)
|
||||||
|
return eqM(w, iz) || eqNM(w, negM(iz))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
export const cis = match(NumberT, Returns(Complex(NumberT), t => ({
|
||||||
|
re: Math.cos(t), im: Math.sin(t)})))
|
||||||
|
|
9
src/complex/utils.js
Normal file
9
src/complex/utils.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import {Complex} from './Complex.js'
|
||||||
|
import {match} from '#core/TypePatterns.js'
|
||||||
|
import {ReturnsAs} from '#generic/helpers.js'
|
||||||
|
|
||||||
|
export const isReal = match(Complex, (math, C) => {
|
||||||
|
const eq = math.equal.resolve([C.Component, C.Component])
|
||||||
|
const add = math.add.resolve([C.Component, C.Component])
|
||||||
|
return ReturnsAs(eq, z => eq(z.re, add(z.re, z.im)))
|
||||||
|
})
|
13
src/core/Implementations.js
Normal file
13
src/core/Implementations.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
export class Implementations {
|
||||||
|
constructor(impOrImps) {
|
||||||
|
if (Array.isArray(impOrImps)) {
|
||||||
|
this.matchers = impOrImps
|
||||||
|
} else this.matchers = [impOrImps]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ImplementationsGenerator {
|
||||||
|
constructor(f) {
|
||||||
|
this.generate = f
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,4 @@
|
||||||
import ArrayKeyedMap from 'array-keyed-map'
|
import ArrayKeyedMap from 'array-keyed-map'
|
||||||
import {match} from './helpers.js'
|
|
||||||
import {Passthru} from './TypePatterns.js'
|
|
||||||
|
|
||||||
// Generic types are callable, so we have no choice but to extend Function
|
// Generic types are callable, so we have no choice but to extend Function
|
||||||
export class Type extends Function {
|
export class Type extends Function {
|
||||||
|
@ -12,9 +10,8 @@ export class Type extends Function {
|
||||||
// let the proxy out of this function, never `this`.
|
// let the proxy out of this function, never `this`.
|
||||||
const rewired = new Proxy(this, {
|
const rewired = new Proxy(this, {
|
||||||
apply: (target, thisForCall, args) => {
|
apply: (target, thisForCall, args) => {
|
||||||
const callThrough = thisForCall ?? target
|
if (target.specialize) return target.specialize(...args)
|
||||||
if (callThrough.specialize) return callThrough.specialize(...args)
|
throw new TypeError(`Type ${target} is not generic`)
|
||||||
throw new TypeError(`Type ${callThrough} is not generic`)
|
|
||||||
},
|
},
|
||||||
get: (target, prop, receiver) => {
|
get: (target, prop, receiver) => {
|
||||||
if (prop === 'isAproxy') return true
|
if (prop === 'isAproxy') return true
|
||||||
|
@ -87,13 +84,3 @@ export const whichType = typs => Returns(TypeOfTypes, item => {
|
||||||
}
|
}
|
||||||
throw new TypeError(errorMsg)
|
throw new TypeError(errorMsg)
|
||||||
})
|
})
|
||||||
|
|
||||||
export const typeOf = match(Passthru, math => whichType(math.types))
|
|
||||||
|
|
||||||
// bootstrapping order matters, but order of exports in a module isn't
|
|
||||||
// simply the order that the items are listed in the module. So we make
|
|
||||||
// an explicitly ordered export of implementations for this sake:
|
|
||||||
|
|
||||||
export const bootstrapTypes = {
|
|
||||||
Type, Undefined, TypeOfTypes, typeOf
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import ArrayKeyedMap from 'array-keyed-map'
|
import ArrayKeyedMap from 'array-keyed-map'
|
||||||
|
|
||||||
|
import {ResolutionError, isPlainFunction, isPlainObject} from './helpers.js'
|
||||||
|
import {Implementations, ImplementationsGenerator} from './Implementations.js'
|
||||||
|
import {bootstrapTypes} from './type.js'
|
||||||
|
import {Returns, whichType, Type} from './Type.js'
|
||||||
import {
|
import {
|
||||||
Implementations, ImplementationsGenerator, Matcher, ResolutionError,
|
matched, needsCollection, Passthru, Matcher, match
|
||||||
isPlainFunction, isPlainObject, match, types
|
} from './TypePatterns.js'
|
||||||
} from './helpers.js'
|
|
||||||
import {bootstrapTypes, Returns, whichType, Type} from './Type.js'
|
const underResolution = Symbol('underResolution')
|
||||||
import {matched, needsCollection, Passthru} from './TypePatterns.js'
|
|
||||||
|
|
||||||
export class TypeDispatcher {
|
export class TypeDispatcher {
|
||||||
constructor(...specs) {
|
constructor(...specs) {
|
||||||
|
@ -14,7 +17,6 @@ export class TypeDispatcher {
|
||||||
this._behaviors = {} // maps key to a map from type vectors to results
|
this._behaviors = {} // maps key to a map from type vectors to results
|
||||||
this._fallbacks = {} // maps key to a catchall result
|
this._fallbacks = {} // maps key to a catchall result
|
||||||
// bootstrap the instance
|
// bootstrap the instance
|
||||||
this.merge({types})
|
|
||||||
this.merge(bootstrapTypes)
|
this.merge(bootstrapTypes)
|
||||||
for (const spec of specs) this.merge(spec)
|
for (const spec of specs) this.merge(spec)
|
||||||
}
|
}
|
||||||
|
@ -261,6 +263,7 @@ export class TypeDispatcher {
|
||||||
if (!(key in this)) {
|
if (!(key in this)) {
|
||||||
throw new ReferenceError(`no method or value for key '${key}'`)
|
throw new ReferenceError(`no method or value for key '${key}'`)
|
||||||
}
|
}
|
||||||
|
if (!Array.isArray(types)) types = [types]
|
||||||
|
|
||||||
const generatingDeps = this.resolve._genDepsOf?.length
|
const generatingDeps = this.resolve._genDepsOf?.length
|
||||||
if (generatingDeps) this._addToDeps(key, types)
|
if (generatingDeps) this._addToDeps(key, types)
|
||||||
|
@ -269,6 +272,10 @@ export class TypeDispatcher {
|
||||||
// Return the cached resolution if it's there
|
// Return the cached resolution if it's there
|
||||||
if (behave.has(types)) {
|
if (behave.has(types)) {
|
||||||
const result = behave.get(types)
|
const result = behave.get(types)
|
||||||
|
if (result === underResolution) {
|
||||||
|
throw new ResolutionError(
|
||||||
|
`recursive resolution of ${key} on ${types}`)
|
||||||
|
}
|
||||||
if (generatingDeps
|
if (generatingDeps
|
||||||
&& typeof result === 'object'
|
&& typeof result === 'object'
|
||||||
&& !(result instanceof Type)
|
&& !(result instanceof Type)
|
||||||
|
@ -328,13 +335,14 @@ export class TypeDispatcher {
|
||||||
let theBehavior = () => undefined
|
let theBehavior = () => undefined
|
||||||
let finalBehavior
|
let finalBehavior
|
||||||
this.resolve._genDepsOf.push([key, types])
|
this.resolve._genDepsOf.push([key, types])
|
||||||
|
behave.set(types, underResolution)
|
||||||
try { // Used to make sure not to return without popping _genDepsOf
|
try { // Used to make sure not to return without popping _genDepsOf
|
||||||
if (!('returns' in item)) {
|
if (!('returns' in item)) {
|
||||||
// looks like a factory
|
// looks like a factory
|
||||||
try {
|
try {
|
||||||
theBehavior = item(
|
theBehavior = item(
|
||||||
DependencyRecorder(this, '', this, []),
|
DependencyRecorder(this, '', this, []),
|
||||||
matched(template))
|
matched(template, this))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
e.message = `Error in factory for ${key} on ${types} `
|
e.message = `Error in factory for ${key} on ${types} `
|
||||||
+ `(match data ${template}): ${e.message}`
|
+ `(match data ${template}): ${e.message}`
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {Type, Undefined} from './Type.js'
|
import {Type, Undefined} from './Type.js'
|
||||||
|
import {isPlainFunction} from './helpers.js'
|
||||||
|
|
||||||
export class TypePattern {
|
export class TypePattern {
|
||||||
match(typeSequence, options={}) {
|
match(typeSequence, options={}) {
|
||||||
|
@ -64,12 +65,35 @@ class SequencePattern extends TypePattern {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PredicatePattern extends TypePattern {
|
||||||
|
constructor(predicate) {
|
||||||
|
super()
|
||||||
|
this.predicate = predicate
|
||||||
|
}
|
||||||
|
match(typeSequence, options={}) {
|
||||||
|
const position = options.position ?? 0
|
||||||
|
if (position >= typeSequence.length) return [-1, undefined]
|
||||||
|
const actual = typeSequence[position]
|
||||||
|
if (this.predicate(actual)) return [position + 1, actual]
|
||||||
|
return [-1, Undefined]
|
||||||
|
}
|
||||||
|
sampleTypes() {
|
||||||
|
throw new Error('sampleTypes() not yet implemented for PredicatePattern')
|
||||||
|
}
|
||||||
|
equal(other) {
|
||||||
|
return super.equal(other) && this.predicate === other.predicate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const pattern = patternOrSpec => {
|
export const pattern = patternOrSpec => {
|
||||||
if (patternOrSpec instanceof TypePattern) return patternOrSpec
|
if (patternOrSpec instanceof TypePattern) return patternOrSpec
|
||||||
if (patternOrSpec instanceof Type) {
|
if (patternOrSpec instanceof Type) {
|
||||||
return new MatchTypePattern(patternOrSpec)
|
return new MatchTypePattern(patternOrSpec)
|
||||||
}
|
}
|
||||||
if (Array.isArray(patternOrSpec)) return new SequencePattern(patternOrSpec)
|
if (Array.isArray(patternOrSpec)) return new SequencePattern(patternOrSpec)
|
||||||
|
if (isPlainFunction(patternOrSpec)) {
|
||||||
|
return new PredicatePattern(patternOrSpec)
|
||||||
|
}
|
||||||
throw new TypeError(`Can't interpret '${patternOrSpec}' as a type pattern`)
|
throw new TypeError(`Can't interpret '${patternOrSpec}' as a type pattern`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,9 +174,16 @@ class PassthruPattern extends TypePattern {
|
||||||
export const Passthru = new PassthruPattern()
|
export const Passthru = new PassthruPattern()
|
||||||
|
|
||||||
// returns the template just of matched types, dropping any actual types
|
// returns the template just of matched types, dropping any actual types
|
||||||
export const matched = (template) => {
|
export const matched = (template, math) => {
|
||||||
if (Array.isArray(template)) return template.map(matched)
|
if (Array.isArray(template)) {
|
||||||
return template.matched ?? template
|
return template.map(pattern => matched(pattern, math))
|
||||||
|
}
|
||||||
|
if (template.matched) {
|
||||||
|
let convert = template.convertor
|
||||||
|
if (!convert.returns) convert = convert(math, template.actual)
|
||||||
|
return convert.returns || template.matched
|
||||||
|
}
|
||||||
|
return template
|
||||||
}
|
}
|
||||||
|
|
||||||
// checks if the template is just pass-through or needs collection
|
// checks if the template is just pass-through or needs collection
|
||||||
|
@ -163,3 +194,12 @@ export const needsCollection = (template) => {
|
||||||
}
|
}
|
||||||
return 'actual' in template
|
return 'actual' in template
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class Matcher {
|
||||||
|
constructor(spec, facOrBehave) {
|
||||||
|
this.pattern = pattern(spec)
|
||||||
|
this.does = facOrBehave
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const match = (spec, facOrBehave) => new Matcher(spec, facOrBehave)
|
||||||
|
|
|
@ -4,8 +4,8 @@ import * as booleans from '#boolean/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 {NumberT} from '#number/NumberT.js'
|
import {NumberT} from '#number/NumberT.js'
|
||||||
import {match, ResolutionError} from "#core/helpers.js"
|
import {ResolutionError} from "#core/helpers.js"
|
||||||
import {Any} from "#core/TypePatterns.js"
|
import {match, Any} from "#core/TypePatterns.js"
|
||||||
import {Returns, NotAType} from "#core/Type.js"
|
import {Returns, NotAType} from "#core/Type.js"
|
||||||
import {plain} from "#number/helpers.js"
|
import {plain} from "#number/helpers.js"
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import assert from 'assert'
|
import assert from 'assert'
|
||||||
import {
|
import {isPlainObject, isPlainFunction} from '../helpers.js'
|
||||||
Matcher, match, isPlainObject, isPlainFunction, Implementations
|
import {Implementations} from '../Implementations.js'
|
||||||
} from '../helpers.js'
|
|
||||||
import {Type, Undefined, TypeOfTypes} from '../Type.js'
|
import {Type, Undefined, TypeOfTypes} from '../Type.js'
|
||||||
import {TypePattern} from '../TypePatterns.js'
|
import {match, Matcher, TypePattern} from '../TypePatterns.js'
|
||||||
|
|
||||||
describe('Core helpers', () => {
|
describe('Core helpers', () => {
|
||||||
it('defines what Matchers are', () => {
|
it('defines what Matchers are', () => {
|
||||||
|
|
|
@ -1,41 +1,3 @@
|
||||||
import {pattern, Passthru} from './TypePatterns.js'
|
|
||||||
|
|
||||||
export class Matcher {
|
|
||||||
constructor(spec, facOrBehave) {
|
|
||||||
this.pattern = pattern(spec)
|
|
||||||
this.does = facOrBehave
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Implementations {
|
|
||||||
constructor(impOrImps) {
|
|
||||||
if (Array.isArray(impOrImps)) {
|
|
||||||
this.matchers = impOrImps
|
|
||||||
} else this.matchers = [impOrImps]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const match = (spec, facOrBehave) => new Matcher(spec, facOrBehave)
|
|
||||||
|
|
||||||
export class ImplementationsGenerator {
|
|
||||||
constructor(f) {
|
|
||||||
this.generate = f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// the archetypal example of needing an ImplementationsGenerator:
|
|
||||||
// each TypeDispatcher must have a types property, which will be a
|
|
||||||
// plain object of types. This must be a different object for each
|
|
||||||
// TypeDispatcher, but the same object regardless of the types vector
|
|
||||||
// passed to resolve. So an ordinary factory won't work, because it
|
|
||||||
// would make a new plain object for each different types vector that
|
|
||||||
// the property `types` was resolved with. And just a plain object
|
|
||||||
// wouldn't work, because then every TypeDispatcher would have the same
|
|
||||||
// collection of types (and modifying the types in one would affect them
|
|
||||||
// all). Hence we do:
|
|
||||||
|
|
||||||
export const types = new ImplementationsGenerator(() => match(Passthru, {}))
|
|
||||||
|
|
||||||
export class ResolutionError extends TypeError {
|
export class ResolutionError extends TypeError {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
super(...args)
|
super(...args)
|
||||||
|
|
26
src/core/type.js
Normal file
26
src/core/type.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import {ImplementationsGenerator} from './Implementations.js'
|
||||||
|
import {Type, TypeOfTypes, Undefined, whichType} from './Type.js'
|
||||||
|
import {match, Passthru} from './TypePatterns.js'
|
||||||
|
|
||||||
|
export const typeOf = match(Passthru, math => whichType(math.types))
|
||||||
|
|
||||||
|
// And now the types object itself: This property provides the archetypal
|
||||||
|
// example of needing an ImplementationsGenerator. Each TypeDispatcher must
|
||||||
|
// have a types property, which will be a plain object of types. This object
|
||||||
|
// must be different for each TypeDispatcher, but within a TypeDispatcher,
|
||||||
|
// it must be the same object regardless of the types vector passed to resolve.
|
||||||
|
// So an ordinary factory won't work, because it would make a new plain object
|
||||||
|
// for each different types vector that the property `types` was resolved with.
|
||||||
|
// And just a plain object wouldn't work, because then every TypeDispatcher
|
||||||
|
// would have the same collection of types (and modifying the types in one
|
||||||
|
// would affect them all). Hence we do:
|
||||||
|
|
||||||
|
export const types = new ImplementationsGenerator(() => match(Passthru, {}))
|
||||||
|
|
||||||
|
// bootstrapping order matters, but order of exports in a module isn't
|
||||||
|
// simply the order that the items are listed in the module. So we make
|
||||||
|
// an explicitly ordered export of implementations for this sake:
|
||||||
|
|
||||||
|
export const bootstrapTypes = {
|
||||||
|
types, Type, Undefined, TypeOfTypes, typeOf
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import {match} from '#core/helpers.js'
|
|
||||||
import {TypeOfTypes, Undefined} from '#core/Type.js'
|
import {TypeOfTypes, Undefined} from '#core/Type.js'
|
||||||
|
import {match} from '#core/TypePatterns.js'
|
||||||
import {boolnum} from '#number/helpers.js'
|
import {boolnum} from '#number/helpers.js'
|
||||||
|
|
||||||
export const indistinguishable = [
|
export const indistinguishable = [
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import {match} from '#core/helpers.js'
|
|
||||||
import {NotAType, Returns, TypeOfTypes} from '#core/Type.js'
|
import {NotAType, Returns, TypeOfTypes} from '#core/Type.js'
|
||||||
import {Any} from "#core/TypePatterns.js"
|
import {match,Any} from "#core/TypePatterns.js"
|
||||||
import {boolnum} from "#number/helpers.js"
|
import {boolnum} from "#number/helpers.js"
|
||||||
|
|
||||||
export const zero = [
|
export const zero = [
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import assert from 'assert'
|
import assert from 'assert'
|
||||||
import math from '#nanomath'
|
import math from '#nanomath'
|
||||||
|
import {NumberT} from '#number/NumberT.js'
|
||||||
|
|
||||||
describe('generic utility functions', () => {
|
describe('generic utility functions', () => {
|
||||||
it('tests whether an element is zero', () => {
|
it('tests whether an element is zero', () => {
|
||||||
|
@ -9,5 +10,16 @@ describe('generic utility functions', () => {
|
||||||
assert(isZero(false))
|
assert(isZero(false))
|
||||||
assert(!isZero(true))
|
assert(!isZero(true))
|
||||||
assert(isZero(undefined))
|
assert(isZero(undefined))
|
||||||
|
assert(isZero(math.types.Complex(NumberT).zero))
|
||||||
|
assert(isZero(math.complex(-2e-16, 4e-17)))
|
||||||
|
assert(!isZero(math.complex(true)))
|
||||||
|
})
|
||||||
|
it('tests whether an element is real', () => {
|
||||||
|
const {isReal} = math
|
||||||
|
assert(isReal(Infinity))
|
||||||
|
assert(isReal(false))
|
||||||
|
assert(isReal(math.types.Complex(NumberT).one))
|
||||||
|
assert(isReal(math.complex(-3.25, 4e-16)))
|
||||||
|
assert(!isReal(math.complex(3, 4)))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {match} from '#core/helpers.js'
|
|
||||||
import {Returns} from '#core/Type.js'
|
import {Returns} from '#core/Type.js'
|
||||||
import {Any} from '#core/TypePatterns.js'
|
import {match, Any} from '#core/TypePatterns.js'
|
||||||
|
|
||||||
|
export const conj = match(Any, (_math, T) => Returns(T, a => a))
|
||||||
export const square = match(Any, (math, T) => {
|
export const square = match(Any, (math, T) => {
|
||||||
const mult = math.multiply.resolve([T, T])
|
const mult = math.multiply.resolve([T, T])
|
||||||
return Returns(mult.returns, a => mult(a, a))
|
return Returns(mult.returns, a => mult(a, a))
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {ImplementationsGenerator, match} from '#core/helpers.js'
|
import {ImplementationsGenerator} from '#core/Implementations.js'
|
||||||
import {Passthru} from '#core/TypePatterns.js'
|
import {match, Passthru} from '#core/TypePatterns.js'
|
||||||
|
|
||||||
export const config = new ImplementationsGenerator(
|
export const config = new ImplementationsGenerator(
|
||||||
() => match(Passthru, {relTol: 1e-12, absTol: 1e-15}))
|
() => match(Passthru, {relTol: 1e-12, absTol: 1e-15}))
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import {ReturnsAs} from './helpers.js'
|
import {ReturnsAs} from './helpers.js'
|
||||||
import {match} from '#core/helpers.js'
|
|
||||||
import {Returns} from '#core/Type.js'
|
import {Returns} from '#core/Type.js'
|
||||||
import {Any, Passthru, matched} from '#core/TypePatterns.js'
|
import {Any, Passthru, match, matched} from '#core/TypePatterns.js'
|
||||||
import {boolnum} from '#number/helpers.js'
|
import {boolnum} from '#number/helpers.js'
|
||||||
|
|
||||||
export const equal = match([Any, Any], (math, [T, U]) => {
|
export const equal = match([Any, Any], (math, [T, U]) => {
|
||||||
|
@ -18,7 +17,7 @@ export const equal = match([Any, Any], (math, [T, U]) => {
|
||||||
return boolnum(() => false)(math)
|
return boolnum(() => false)(math)
|
||||||
}
|
}
|
||||||
// Get the type of the first argument to the matching checker:
|
// Get the type of the first argument to the matching checker:
|
||||||
const ByType = matched(exactChecker.template).flat()[0]
|
const ByType = matched(exactChecker.template, math).flat()[0]
|
||||||
// Now see if there are tolerances for that type:
|
// Now see if there are tolerances for that type:
|
||||||
const typeConfig = math.resolve('config', [ByType])
|
const typeConfig = math.resolve('config', [ByType])
|
||||||
if ('relTol' in typeConfig) {
|
if ('relTol' in typeConfig) {
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import {ReturnsAs} from './helpers.js'
|
import {ReturnsAs} from './helpers.js'
|
||||||
import {ResolutionError, match} from '#core/helpers.js'
|
import {ResolutionError} from '#core/helpers.js'
|
||||||
import {Returns} from '#core/Type.js'
|
import {Passthru, match} from '#core/TypePatterns.js'
|
||||||
import {Passthru} from "#core/TypePatterns.js"
|
import {boolnum} from '#number/helpers.js'
|
||||||
|
|
||||||
|
// Most types are real. Have to make sure to redefine on all non-real types
|
||||||
|
export const isReal = match(Passthru, boolnum(() => true))
|
||||||
export const isZero = match(Passthru, (math, [T]) => {
|
export const isZero = match(Passthru, (math, [T]) => {
|
||||||
if (!T) { // called with no arguments
|
if (!T) { // called with no arguments
|
||||||
throw new ResolutionError('isZero() requires one argument')
|
throw new ResolutionError('isZero() requires one argument')
|
||||||
|
|
|
@ -5,6 +5,16 @@ import * as numbers from './number/all.js'
|
||||||
import * as complex from './complex/all.js'
|
import * as complex from './complex/all.js'
|
||||||
import {TypeDispatcher} from '#core/TypeDispatcher.js'
|
import {TypeDispatcher} from '#core/TypeDispatcher.js'
|
||||||
|
|
||||||
const math = new TypeDispatcher(booleans, coretypes, generics, numbers, complex)
|
// At the moment, since we are not sorting patterns in any way,
|
||||||
|
// order matters in the construction. Patterns that come later in
|
||||||
|
// the following list will be tried earlier. (The rationale for that
|
||||||
|
// ordering is that any time you merge something, it should supersede
|
||||||
|
// 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_
|
||||||
|
// non-complex type to complex, potentially making a poor overload choice)
|
||||||
|
// to be tried last.
|
||||||
|
const math = new TypeDispatcher(generics, complex, booleans, coretypes, numbers)
|
||||||
|
|
||||||
export default math
|
export default math
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import {Type} from '#core/Type.js'
|
import {Type} from '#core/Type.js'
|
||||||
import {match} from '#core/helpers.js'
|
import {match} from '#core/TypePatterns.js'
|
||||||
import {BooleanT} from '#boolean/BooleanT.js'
|
import {BooleanT} from '#boolean/BooleanT.js'
|
||||||
|
|
||||||
export const NumberT = new Type(n => typeof n === 'number', {
|
export const NumberT = new Type(n => typeof n === 'number', {
|
||||||
from: match(BooleanT, math => math.number.resolve([BooleanT])),
|
from: match(BooleanT, math => math.number.resolve([BooleanT])),
|
||||||
|
typeName: 'NumberT', // since used before ever put in a TypeDispatcher
|
||||||
one: 1,
|
one: 1,
|
||||||
zero: 0,
|
zero: 0,
|
||||||
nan: NaN
|
nan: NaN
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {NumberT} from './NumberT.js'
|
import {NumberT} from './NumberT.js'
|
||||||
|
|
||||||
import {match} from '#core/helpers.js'
|
|
||||||
import {Returns} from '#core/Type.js'
|
import {Returns} from '#core/Type.js'
|
||||||
|
import {match} from '#core/TypePatterns.js'
|
||||||
|
|
||||||
export const plain = f => match(
|
export const plain = f => match(
|
||||||
Array(f.length).fill(NumberT), Returns(NumberT, f))
|
Array(f.length).fill(NumberT), Returns(NumberT, f))
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import {match} from '#core/helpers.js'
|
|
||||||
import {Returns} from '#core/Type.js'
|
import {Returns} from '#core/Type.js'
|
||||||
import {Optional} from '#core/TypePatterns.js'
|
import {match, Optional} from '#core/TypePatterns.js'
|
||||||
import {boolnum} from './helpers.js'
|
import {boolnum} from './helpers.js'
|
||||||
import {NumberT} from './NumberT.js'
|
import {NumberT} from './NumberT.js'
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {plain} from './helpers.js'
|
import {plain} from './helpers.js'
|
||||||
import {BooleanT} from '#boolean/BooleanT.js'
|
import {BooleanT} from '#boolean/BooleanT.js'
|
||||||
import {match} from '#core/helpers.js'
|
|
||||||
import {Returns} from '#core/Type.js'
|
import {Returns} from '#core/Type.js'
|
||||||
|
import {match} from '#core/TypePatterns.js'
|
||||||
import {NumberT} from '#number/NumberT.js'
|
import {NumberT} from '#number/NumberT.js'
|
||||||
|
|
||||||
const num = f => Returns(NumberT, f)
|
const num = f => Returns(NumberT, f)
|
||||||
|
|
|
@ -2,7 +2,7 @@ import {plain, boolnum} from './helpers.js'
|
||||||
import {NumberT} from './NumberT.js'
|
import {NumberT} from './NumberT.js'
|
||||||
|
|
||||||
import {Returns} from '#core/Type.js'
|
import {Returns} from '#core/Type.js'
|
||||||
import {match} from '#core/helpers.js'
|
import {match} from '#core/TypePatterns.js'
|
||||||
|
|
||||||
export const clone = plain(a => a)
|
export const clone = plain(a => a)
|
||||||
export const isnan = match(NumberT, boolnum(isNaN))
|
export const isnan = match(NumberT, boolnum(isNaN))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue