experiment: convert all implementations to plain types

This commit is contained in:
Jos de Jong 2022-12-23 11:27:39 +01:00
parent 8c06c8f36e
commit cbd1719227
14 changed files with 198 additions and 435 deletions

View File

@ -1,10 +1,3 @@
import {ForType} from '../core/Dispatcher.js'
import {ComplexReturn} from './type.js'
import * as Complex from './native.js' import * as Complex from './native.js'
export {Complex} export {Complex}
declare module "../core/Dispatcher" {
interface ReturnTypes<Params>
extends ForType<'Complex', ComplexReturn<Params>> {}
}

View File

@ -1,109 +1,95 @@
import {Complex, UnderlyingReal, complex_binary} from './type.js' import { Complex, complex_binary } from './type.js'
import {
BBinary, Dependency, ConservativeUnary, ConservativeBinary, ImpType
} from '../core/Dispatcher.js'
declare module "./type" {
interface ComplexReturn<Params> {
add: ConservativeBinary<Params, Complex<any>>
addReal: Params extends [infer Z, infer R]
? [R] extends [UnderlyingReal<Z>] ? Z : never
: never
unaryMinus: ConservativeUnary<Params, Complex<any>>
conj: ConservativeUnary<Params, Complex<any>>
subtract: ConservativeBinary<Params, Complex<any>>
multiply: ConservativeBinary<Params, Complex<any>>
absquare: Params extends [infer Z]
? Z extends Complex<any> ? UnderlyingReal<Z> : never
: never
reciprocal: ConservativeUnary<Params, Complex<any>>
divide: ConservativeBinary<Params, Complex<any>>
divideByReal: Params extends [infer Z, infer R]
? [R] extends [UnderlyingReal<Z>] ? Z : never
: never
// square root that remains the same type
conservativeSqrt: ConservativeUnary<Params, Complex<any>>
// Same as conservativeSqrt for complex numbers:
sqrt: ConservativeUnary<Params, Complex<any>>
// complex square root of the real type of a complex:
complexSqrt: Params extends [infer T] ? Complex<T> : never
}
}
export const add = export const add =
<T>(dep: Dependency<'add', [T,T]>): <T>(dep: {
ImpType<'add', [Complex<T>, Complex<T>]> => add: (a: T, b: T) => T
(w, z) => complex_binary(dep.add(w.re, z.re), dep.add(w.im, z.im)) }) =>
(w: Complex<T>, z: Complex<T>): Complex<T> =>
complex_binary(dep.add(w.re, z.re), dep.add(w.im, z.im))
export const addReal = export const addReal =
<T>(dep: Dependency<'addReal', [T, UnderlyingReal<T>]>): <T>(dep: {
ImpType<'addReal', [Complex<T>, UnderlyingReal<T>]> => addReal: (a: T, b: T) => T
(z, r) => complex_binary(dep.addReal(z.re, r), z.im) }) =>
(z: Complex<T>, r: T): Complex<T> =>
complex_binary(dep.addReal(z.re, r), z.im)
export const unaryMinus = export const unaryMinus =
<T>(dep: Dependency<'unaryMinus', [T]>): <T>(dep: {
ImpType<'unaryMinus', [Complex<T>]> => unaryMinus: (z: T) => T
z => complex_binary(dep.unaryMinus(z.re), dep.unaryMinus(z.im)) }) =>
(z: Complex<T>): Complex<T> =>
complex_binary(dep.unaryMinus(z.re), dep.unaryMinus(z.im))
export const conj = export const conj =
<T>(dep: Dependency<'unaryMinus'|'conj', [T]>): <T>(dep: {
ImpType<'conj', [Complex<T>]> => unaryMinus: (z: T) => T,
z => complex_binary(dep.conj(z.re), dep.unaryMinus(z.im)) conj: (z: T) => T
}) =>
(z: Complex<T>): Complex<T> =>
complex_binary(dep.conj(z.re), dep.unaryMinus(z.im))
export const subtract = export const subtract =
<T>(dep: Dependency<'subtract', [T,T]>): <T>(dep: {
ImpType<'subtract', [Complex<T>, Complex<T>]> => subtract: (a: T, b: T) => T
(w, z) => complex_binary(dep.subtract(w.re, z.re), dep.subtract(w.im, z.im)) }) =>
(w: Complex<T>, z: Complex<T>): Complex<T> =>
complex_binary(dep.subtract(w.re, z.re), dep.subtract(w.im, z.im))
export const multiply = export const multiply =
<T>(dep: Dependency<'add', [T,T]> <T>(dep: {
& Dependency<'subtract', [T,T]> add: (a: T, b: T) => T,
& Dependency<'multiply', [T,T]> subtract: (a: T, b: T) => T,
& Dependency<'conj', [T]>): multiply: (a: T, b: T) => T,
ImpType<'multiply', [Complex<T>, Complex<T>]> => conj: (z: T) => T
(w, z) => { }) =>
(w: Complex<T>, z: Complex<T>): Complex<T> => {
const mult = dep.multiply const mult = dep.multiply
const realpart = dep.subtract( const realpart = dep.subtract(mult(w.re, z.re), mult(dep.conj(w.im), z.im))
mult( w.re, z.re), mult(dep.conj(w.im), z.im)) const imagpart = dep.add(mult(dep.conj(w.re), z.im), mult(w.im, z.re))
const imagpart = dep.add(
mult(dep.conj(w.re), z.im), mult( w.im, z.re))
return complex_binary(realpart, imagpart) return complex_binary(realpart, imagpart)
} }
export const absquare = export const absquare =
<T>(dep: Dependency<'absquare', [T]> <T>(dep: {
& Dependency<'add', BBinary<UnderlyingReal<T>>>): add: (a: T, b: T) => T,
ImpType<'absquare', [Complex<T>]> => absquare: (z: T) => T
z => dep.add(dep.absquare(z.re), dep.absquare(z.im)) }) =>
(z: Complex<T>): T => dep.add(dep.absquare(z.re), dep.absquare(z.im))
export const divideByReal = export const divideByReal =
<T>(dep: Dependency<'divideByReal', [T, UnderlyingReal<T>]>): <T>(dep: {
ImpType<'divideByReal', [Complex<T>, UnderlyingReal<T>]> => divideByReal: (a: T, b: T) => T
(z, r) => complex_binary( }) =>
dep.divideByReal(z.re, r), dep.divideByReal(z.im, r)) (z: Complex<T>, r: T) =>
complex_binary(dep.divideByReal(z.re, r), dep.divideByReal(z.im, r))
export const reciprocal = export const reciprocal =
<T>(dep: Dependency<'conj', [Complex<T>]> <T>(dep: {
& Dependency<'absquare', [Complex<T>]> conj: (z: Complex<T>) => Complex<T>,
& Dependency<'divideByReal', [Complex<T>, UnderlyingReal<T>]>): absquare: (z: Complex<T>) => T,
ImpType<'reciprocal', [Complex<T>]> => divideByReal: (a: Complex<T>, b: T) => Complex<T>,
z => dep.divideByReal(dep.conj(z), dep.absquare(z)) zero: (z: T) => T,
}) =>
(z: Complex<T>): Complex<T> => dep.divideByReal(dep.conj(z), dep.absquare(z))
export const divide = export const divide =
<T>(dep: Dependency<'multiply', [Complex<T>, Complex<T>]> <T>(dep: {
& Dependency<'reciprocal', [Complex<T>]>): multiply: (a: Complex<T>, b: Complex<T>) => Complex<T>,
ImpType<'divide', [Complex<T>, Complex<T>]> => reciprocal: (z: Complex<T>) => Complex<T>,
(w, z) => dep.multiply(w, dep.reciprocal(z)) }) =>
(w: Complex<T>, z: Complex<T>) => dep.multiply(w, dep.reciprocal(z))
export const complexSqrt = export const complexSqrt =
<T>(dep: Dependency<'conservativeSqrt', [T]> <T>(dep: {
& Dependency<'isSquare', [T]> conservativeSqrt: (a: T) => T,
& Dependency<'complex', [T]> isSquare: (a: T) => boolean,
& Dependency<'unaryMinus', [T]> complex: (a: T) => Complex<T>,
& Dependency<'zero', [T]> unaryMinus: (a: T) => T,
& Dependency<'nan', [Complex<T>]>): ImpType<'complexSqrt', [T]> => zero: (a: T) => T,
r => { nan: (a: Complex<T>) => Complex<T>
}) =>
(r: T): Complex<T> => {
if (dep.isSquare(r)) return dep.complex(dep.conservativeSqrt(r)) if (dep.isSquare(r)) return dep.complex(dep.conservativeSqrt(r))
const negative = dep.unaryMinus(r) const negative = dep.unaryMinus(r)
if (dep.isSquare(negative)) { if (dep.isSquare(negative)) {
@ -117,16 +103,18 @@ export const complexSqrt =
} }
export const sqrt = export const sqrt =
<T>(dep: Dependency<'isReal', [Complex<T>]> <T>(dep: {
& Dependency<'complexSqrt', [T]> isReal: (z: Complex<T>) => boolean,
& Dependency<'absquare', [Complex<T>]> complexSqrt: (a: T) => Complex<T>,
& Dependency<'conservativeSqrt', [UnderlyingReal<T>]> conservativeSqrt: (a: T) => T,
& Dependency<'addReal', [Complex<T>,UnderlyingReal<T>]> absquare: (a: Complex<T>) => T,
& Dependency<'re', [Complex<T>]> addReal: (a: Complex<T>, b: T) => Complex<T>,
& Dependency<'add', [UnderlyingReal<T>,UnderlyingReal<T>]> divideByReal: (a: Complex<T>, b: T) => Complex<T>,
& Dependency<'divideByReal', [Complex<T>,UnderlyingReal<T>]> add: (a: T, b: T) => T,
): ImpType<'sqrt', [Complex<T>]> => re: (a: Complex<T>) => T,
z => {
}) =>
(z: Complex<T>) => {
if (dep.isReal(z)) return dep.complexSqrt(z.re) if (dep.isReal(z)) return dep.complexSqrt(z.re)
const myabs = dep.conservativeSqrt(dep.absquare(z)) const myabs = dep.conservativeSqrt(dep.absquare(z))
const num = dep.addReal(z, myabs) const num = dep.addReal(z, myabs)

View File

@ -1,19 +1,12 @@
import { Complex } from './type.js' import { Complex } from './type.js'
import {Signature, Dependency, ImpType} from '../core/Dispatcher.js'
declare module "./type" {
interface ComplexReturn<Params> {
isReal: Signature<Params, [Complex<any>], boolean>
isSquare: Signature<Params, [Complex<any>], boolean>
}
}
export const isReal = export const isReal =
<T>(dep: Dependency<'equal', [T,T]> <T>(dep: {
& Dependency<'add', [T,T]> equal: (a: T, b: T) => boolean,
& Dependency<'isReal', [T]> add: (a: T, b: T) => T,
): ImpType<'isReal', [Complex<T>]> => isReal: (z: T) => boolean
z => dep.isReal(z.re) && dep.equal(z.re, dep.add(z.re, z.im)) }) =>
(z: Complex<T>) => dep.isReal(z.re) && dep.equal(z.re, dep.add(z.re, z.im))
export const isSquare: ImpType<'isSquare', [Complex<any>]> = export const isSquare =
z => true // FIXME: not correct for Complex<bigint> once we get there <T>(z: Complex<T>) => true // FIXME: not correct for Complex<bigint> once we get there

View File

@ -1,15 +1,7 @@
import { Complex } from './type.js' import { Complex } from './type.js'
import {BBinary, ImpType, Dependency} from '../core/Dispatcher.js'
declare module "./type" {
interface ComplexReturn<Params> {
equal: Params extends BBinary<infer B>
? B extends Complex<any> ? boolean : never
: never
}
}
export const equal = export const equal =
<T>(dep: Dependency<'equal', [T,T]>): <T>(dep: {
ImpType<'equal', [Complex<T>, Complex<T>]> => equal: (a: T, b: T) => boolean
(w, z) => dep.equal(w.re, z.re) && dep.equal(w.im, z.im) }) =>
(w: Complex<T>, z: Complex<T>): boolean => dep.equal(w.re, z.re) && dep.equal(w.im, z.im)

View File

@ -1,12 +1,9 @@
import { import {
joinTypes, typeOfDependency, Dependency, BBinary, ImpType, ImpReturns joinTypes, typeOfDependency, Dependency,
} from '../core/Dispatcher.js' } from '../core/Dispatcher.js'
export type Complex<T> = { re: T; im: T; } export type Complex<T> = { re: T; im: T; }
export type UnderlyingReal<T> =
T extends Complex<infer U> ? UnderlyingReal<U> : T
export const Complex_type = { export const Complex_type = {
test: <T>(dep: { testT: (z: unknown) => z is T }) => test: <T>(dep: { testT: (z: unknown) => z is T }) =>
(z: unknown): z is Complex<T> => (z: unknown): z is Complex<T> =>
@ -22,66 +19,36 @@ export const Complex_type = {
} }
} }
export interface ComplexReturn<Params> {
// Sadly, I can't think of a way to make some nice abbreviation operators
// for these generic type specifications because TypeScript generics
// can't take and use generic parameters, only fully instantiated types.
complex: Params extends [infer U] ? Complex<U> // unary case
: Params extends BBinary<infer B> ? Complex<B> // binary case
: never
// alternatively if it seems better; each definition is simpler, but at
// the cost of having two keys here:
// complex_unary: Params extends [infer R] ? Complex<R> : never
// complex_binary: Params extends BBinary<infer R> ? Complex<R> : never
// There is actually a subtlety here that complex_unary really only works
// on real types that include their own zero value, so it should really be
// complex_unary: Params extends [infer R]
// ? ImpReturns<'zero', [R]> extends R ? Complex<R> : never
// : never
// and that might actually simplify some of the typings of other operations,
// but we'll leave such fine tuning til later, if we adopt this scheme
zero: Params extends [infer Z] // unary
? Z extends Complex<infer T> // of a Complex parameter
? ImpReturns<'zero', T> extends T ? Z : never // that has its real 0
: never
: never
one: Params extends [infer Z] // unary
? Z extends Complex<infer T> // of a Complex parameter
? ImpReturns<'one'|'zero', T> extends T ? Z : never // has real 1, 0
: never
: never
nan: Params extends [infer Z] // unary
? Z extends Complex<infer T> // of a Complex parameter
? ImpReturns<'nan', T> extends T ? Z : never // has real NaN
: never
: never
re: Params extends [infer Z]
? Z extends Complex<infer T> ? UnderlyingReal<T> : never
: never
}
export const complex_unary = export const complex_unary =
<T>(dep: Dependency<'zero', [T]>): ImpType<'complex', [T]> => <T>(dep: {
t => ({re: t, im: dep.zero(t)}) zero: (z: T) => Complex<T>
export const complex_binary = <T>(t: T, u: T): ImpReturns<'complex', [T,T]> => }) =>
({re: t, im: u}) (t: T) => ({ re: t, im: dep.zero(t) })
export const complex_binary =
<T>(t: T, u: T): Complex<T> => ({ re: t, im: u })
export const zero = export const zero =
<T>(dep: Dependency<'zero', [T]>): ImpType<'zero', [Complex<T>]> => <T>(dep: {
z => complex_binary(dep.zero(z.re), dep.zero(z.im)) zero: (z: T) => T
}) =>
(z: Complex<T>): Complex<T> => complex_binary(dep.zero(z.re), dep.zero(z.im))
export const one = export const one =
<T>(dep: Dependency<'zero' | 'one', [T]>): ImpType<'one', [Complex<T>]> => <T>(dep: {
z => // Must provide parameter T, else TS narrows to return type of dep.one zero: (z: T) => T,
complex_binary<T>(dep.one(z.re), dep.zero(z.im)) one: (z: T) => T
}) =>
(z: Complex<T>): Complex<T> => complex_binary(dep.one(z.re), dep.zero(z.im))
export const nan = export const nan =
<T>(dep: Dependency<'nan', [T]>): ImpType<'nan', [Complex<T>]> => <T>(dep: {
z => complex_binary(dep.nan(z.re), dep.nan(z.im)) nan: (z: T) => T
}) =>
(z: Complex<T>): Complex<T> => complex_binary(dep.nan(z.re), dep.nan(z.im))
export const re = export const re =
<T>(dep: Dependency<'re', [T]>): ImpType<'re', [Complex<T>]> => <T>(dep: {
z => dep.re(z.re) re: (z: T) => T
}) =>
(z: Complex<T>): T => dep.re(z.re)

View File

@ -66,30 +66,6 @@ export interface ReturnTypes<Params> {}
export type Signature<CandidateParams, ActualParams, Returns> = export type Signature<CandidateParams, ActualParams, Returns> =
CandidateParams extends ActualParams ? Returns : never CandidateParams extends ActualParams ? Returns : never
// A homogeneous binary parameter tuple (comes up a lot, needs a better name?)
// Typical usage: `foo_impl: Params extends BBinary<infer B> ? B : never`
// says that this implementation takes two arguments, both of type B, and
// returns the same type.
export type BBinary<B> = [B, B]
// A unary signature that preserves the type of its argument, which must
// extend the given Bound:
export type ConservativeUnary<CandidateParams, Bound> =
CandidateParams extends [infer T] ? T extends Bound ? T : never : never
// A homogeneous binary signature that preserves the common type of its
// arguments, which must extend the given Bound:
export type ConservativeBinary<CandidateParams, Bound> =
CandidateParams extends BBinary<infer B>
? B extends Bound ? B : never
: never
// Helper for collecting return types
// (Really just adds the literal string Suffix onto the keys of interface IFace)
export type ForType<Suffix extends string, IFace> = keyof IFace extends string
? {[K in keyof IFace as `${K}_${Suffix}`]: IFace[K]}
: never
//dummy implementation for now //dummy implementation for now
export function joinTypes(a: TypeName, b: TypeName) { export function joinTypes(a: TypeName, b: TypeName) {
if (a === b) return a if (a === b) return a

View File

@ -1,10 +1,3 @@
import { ForType } from '../core/Dispatcher.js'
import { GenericReturn } from './type.js'
import * as generic from './arithmetic.js' import * as generic from './arithmetic.js'
export { generic } export { generic }
declare module "../core/Dispatcher" {
interface ReturnTypes<Params>
extends ForType<'generic', GenericReturn<Params>> { }
}

View File

@ -1,39 +1,5 @@
import {Dependency, ImpType, ImpReturns} from "../core/Dispatcher";
declare module "./type" {
interface GenericReturn<Params> {
// Jos: not sure how to define this or why it is needed
// square: Signature<Params, [T], T>
// square: ConservativeUnary<Params, T>
// square: Params extends [infer R]
// ? R extends number ? UnderlyingReal<R> : never
// : never
// The type of `square` in this interface, instantiated with the type
// Params of a parameter list, needs to be the return type of the
// operation `square` on those parameters. In other words, `square` gives
// a type transformer from the tuple type of its parameters to its return
// type.
// That's how Dispatcher knows what the return type will be in
// `Dependency<'square', [bigint]>`, for example: it instantiates
// GenericReturn with Params equal to [bigint] and then grabs the
// type of the `square` property. Hence we write:
square: Params extends [infer T] // square only takes 1 arbitrary parameter
? ImpReturns<'multiply', [T, T]> // and returns whatever multiply does
: never; // otherwise if not a single argument, this implementation
// doesn't handle it
// If square had more than one implementation in this collection, we could
// either add more conditional clauses to the above type transformer
// as I did in Complex/type.ts for `complex`, or we could have two
// different keys that both start with `square_` and Dispatcher will
// check both (as I have now done in comments in Complex/type.ts and
// verified that also works).
}
}
export const square = export const square =
<T>(dep: Dependency<'multiply', [T, T]>): <T>(dep: {
ImpType<'square', [T]> => multiply: (x: T, y: T) => T
z => dep.multiply(z, z) }) =>
(z: T): T => dep.multiply(z, z)

View File

@ -1,3 +0,0 @@
export interface GenericReturn<Params> {
}

View File

@ -1,10 +1,3 @@
import {ForType} from '../core/Dispatcher.js'
import {NumbersReturn} from './type.js'
import * as numbers from './native.js' import * as numbers from './native.js'
export {numbers} export {numbers}
declare module "../core/Dispatcher" {
interface ReturnTypes<Params>
extends ForType<'numbers', NumbersReturn<Params>> {}
}

View File

@ -1,68 +1,24 @@
import {configDependency} from '../core/Config.js' import { Config } from '../core/Config.js'
import { import type { Complex } from '../Complex/type.js'
Signature, ConservativeBinary, ConservativeUnary, Dependency, ImpType
} from '../core/Dispatcher.js'
import type {Complex, UnderlyingReal} from '../Complex/type.js'
declare module "./type" { export const add = (a: number, b: number): number => a + b
interface NumbersReturn<Params> {
// This description loses information: some subtypes like NumInt or
// Positive are closed under addition, but this says that the result
// of add is just a number, not still of the reduced type
// add: Signature<Params, [number, number], number>
// Whereas this one preserves information, but lies
// because it claims all subtypes of number are closed under addition,
// which is not true for `1 | 2 | 3`, for example. But because in
// generics that use add we often need to assign the result of add
// to something of the exact generic type, generics using add won't
// compile unless we lie in this way and assert that add returns
// the subtype.
add: ConservativeBinary<Params, number>
// Not sure how this will need to go when we introduce NumInt.
addReal: Params extends [infer R, infer S]
? R extends number ? S extends R ? R : never : never
: never
unaryMinus: ConservativeUnary<Params, number>
conj: ConservativeUnary<Params, number>
subtract: ConservativeBinary<Params, number>
multiply: ConservativeBinary<Params, number>
absquare: Params extends [infer R]
? R extends number ? UnderlyingReal<R> : never
: never
reciprocal: ConservativeUnary<Params, number>
divide: ConservativeBinary<Params, number>
divideByReal: Params extends [infer R, infer S]
? R extends number ? S extends R ? R : never : never
: never
// best square root that remains the same type
conservativeSqrt: ConservativeUnary<Params, number>
// Best we can do for sqrt at compile time, since actual return
// type depends on config. Not sure how this will play out
// when we make a number-only bundle, but at least the import type
// above for Complex<> does not lead to any emitted JavaScript.
sqrt: Signature<Params, [number], number | Complex<number>>
}
}
export const add: ImpType<'add', [number, number]> = (a, b) => a + b
export const addReal = add export const addReal = add
export const unaryMinus: ImpType<'unaryMinus', [number]> = a => -a export const unaryMinus = (a: number): number => -a
export const conj: ImpType<'conj', [number]> = a => a export const conj = (a: number): number => a
export const subtract: ImpType<'subtract', [number, number]> = (a, b) => a - b export const subtract = (a: number, b: number): number => a - b
export const multiply: ImpType<'multiply', [number, number]> = (a, b) => a * b export const multiply = (a: number, b: number): number => a * b
export const absquare: ImpType<'absquare', [number]> = a => a*a export const absquare = (a: number): number => a * a
export const reciprocal: ImpType<'reciprocal', [number]> = a => 1/a export const reciprocal = (a: number): number => 1 / a
export const divide: ImpType<'divide', [number, number]> = (a, b) => a / b export const divide = (a: number, b: number): number => a / b
export const divideByReal: ImpType<'divideByReal', [number, number]> = divide export const divideByReal = divide
export const conservativeSqrt: ImpType<'conservativeSqrt', [number]> = export const conservativeSqrt = (a: number): number => isNaN(a) ? NaN : Math.sqrt(a)
a => isNaN(a) ? NaN : Math.sqrt(a)
export const sqrt = export const sqrt =
(dep: configDependency (dep: {
& Dependency<'complex', [number, number]>): ImpType<'sqrt', [number]> => { config: Config,
complex: (re: number, im: number) => Complex<number>
}): (a: number) => number | Complex<number> => {
if (dep.config.predictable || !dep.complex) return conservativeSqrt if (dep.config.predictable || !dep.complex) return conservativeSqrt
return a => { return a => {
if (isNaN(a)) return NaN if (isNaN(a)) return NaN

View File

@ -1,11 +1,2 @@
import {Signature, ImpType} from '../core/Dispatcher.js' export const isReal = (a: number) : boolean => true
export const isSquare = (a: number) : boolean => a >= 0
declare module "./type" {
interface NumbersReturn<Params> {
isReal: Signature<Params, [number], true>
isSquare: Signature<Params, [number], boolean>
}
}
export const isReal: ImpType<'isReal', [number]> = a => true
export const isSquare: ImpType<'isSquare', [number]> = a => a >= 0

View File

@ -1,18 +1,11 @@
import {configDependency} from '../core/Config.js' import { Config } from '../core/Config.js'
import {Signature, ImpType, Dependency} from '../core/Dispatcher.js'
const DBL_EPSILON = Number.EPSILON || 2.2204460492503130808472633361816E-16 const DBL_EPSILON = Number.EPSILON || 2.2204460492503130808472633361816E-16
declare module "./type" {
interface NumbersReturn<Params> {
equal: Signature<Params, [number, number], boolean>
unequal: Signature<Params, [number, number], boolean>
}
}
export const equal = export const equal =
(dep: configDependency): ImpType<'equal', [number, number]> => (dep: {
(x, y) => { config: Config
}) => (x: number, y: number): boolean => {
const eps = dep.config.epsilon const eps = dep.config.epsilon
if (eps === null || eps === undefined) return x === y if (eps === null || eps === undefined) return x === y
if (x === y) return true if (x === y) return true
@ -27,8 +20,7 @@ export const equal =
return false return false
} }
export const unequal = (dep: Dependency<'equal', [number, number]>): export const unequal = (dep: {
ImpType<'unequal', [number, number]> => equal: (x: number, y: number) => boolean
(x, y) => { }) =>
return !dep.equal(x, y) (x: number, y: number): boolean => !dep.equal(x, y)
}

View File

@ -1,44 +1,10 @@
import {ImpType} from '../core/Dispatcher.js'
import type {UnderlyingReal} from '../Complex/type.js'
export const number_type = { export const number_type = {
before: ['Complex'], before: ['Complex'],
test: (n: unknown): n is number => typeof n === 'number', test: (n: unknown): n is number => typeof n === 'number',
from: {string: s => +s} from: { string: (s: string) => +s }
} }
export const zero = (a: number): number => 0
export interface NumbersReturn<Params> { export const one = (a: number): number => 1
// The following description of the return type of `zero` on a single export const nan = (a: number): number => NaN
// number argument has ended up unfortunately rather complicated. However, export const re = (a: number): number => a
// it illustrates the typing is really working: Suppose we have a
// `type Small = 1 | 2 | 3`. Then Small indeed extends number, but we
// can't use the operation `zero(s: Small)` because zero is supposed to
// return something of the same type as its argument, but there is no
// zero in Small. Anyhow, in plain language the below says that given
// one parameter of a subtype of number, as long as that subtype includes 0,
// the zero operation returns a member of the type `0` (so we know even
// at compile time that its value will be 0).
zero: Params extends [infer T]
? T extends number ? 0 extends T ? 0 : never : never
: never
// Note that in any case the simple
// zero: Signature<Params, [number], 0>
// makes complex fail to compile, because it worries that you might be
// making `Complex<Small>` where zero would not return the right type.
one: Params extends [infer T]
? T extends number ? 1 extends T ? 1 : never : never
: never
nan: Params extends [infer T]
? T extends number ? typeof NaN extends T ? typeof NaN : never : never
: never
re: Params extends [infer T]
? T extends number ? UnderlyingReal<T> : never
: never
}
export const zero: ImpType<'zero', [number]> = a => 0
export const one: ImpType<'one', [number]> = a => 1
export const nan: ImpType<'nan', [number]> = a => NaN
export const re: ImpType<'re', [number]> = a => a