diff --git a/src/Complex/all.ts b/src/Complex/all.ts index f5369ec..b395743 100644 --- a/src/Complex/all.ts +++ b/src/Complex/all.ts @@ -1,10 +1,10 @@ import {ForType} from '../core/Dispatcher.js' -import {ComplexReturn} from './type.js' +import {ComplexImpTypes} from './type.js' import * as Complex from './native.js' export {Complex} declare module "../core/Dispatcher" { - interface ReturnTypes - extends ForType<'Complex', ComplexReturn> {} + interface ImpTypes + extends ForType<'Complex', ComplexImpTypes> {} } diff --git a/src/Complex/arithmetic.ts b/src/Complex/arithmetic.ts index 8fc32fb..320cbdc 100644 --- a/src/Complex/arithmetic.ts +++ b/src/Complex/arithmetic.ts @@ -1,33 +1,31 @@ import {Complex, UnderlyingReal, complex_binary} from './type.js' -import { - BBinary, Dependency, ConservativeUnary, ConservativeBinary, ImpType -} from '../core/Dispatcher.js' +import {Dependency, ImpType} from '../core/Dispatcher.js' +type ComplexUnary = + T extends Complex ? (a: Complex) => Complex : never +type ComplexBinary = + T extends Complex + ? (a: Complex, b: Complex) => Complex + : never +type ComplexReal = T extends Complex + ? (a: Complex, b: UnderlyingReal) => Complex + : never declare module "./type" { - interface ComplexReturn { - add: ConservativeBinary> - addReal: Params extends [infer Z, infer R] - ? [R] extends [UnderlyingReal] ? Z : never - : never - unaryMinus: ConservativeUnary> - conj: ConservativeUnary> - subtract: ConservativeBinary> - multiply: ConservativeBinary> - absquare: Params extends [infer Z] - ? Z extends Complex ? UnderlyingReal : never - : never - reciprocal: ConservativeUnary> - divide: ConservativeBinary> - divideByReal: Params extends [infer Z, infer R] - ? [R] extends [UnderlyingReal] ? Z : never - : never + interface ComplexImpTypes { + add: ComplexBinary + add_real: ComplexReal + unaryMinus: ComplexUnary + conj: ComplexUnary + subtract: ComplexBinary + multiply: ComplexBinary + absquare: T extends Complex ? (a: T) => UnderlyingReal : never + reciprocal: ComplexUnary + divide: ComplexBinary + divide_real: ComplexReal // square root that remains the same type - conservativeSqrt: ConservativeUnary> + conservativeSqrt: ComplexUnary // Same as conservativeSqrt for complex numbers: - sqrt: ConservativeUnary> - - // complex square root of the real type of a complex: - complexSqrt: Params extends [infer T] ? Complex : never + sqrt: ComplexUnary } } @@ -36,10 +34,10 @@ export const add = ImpType<'add', [Complex, Complex]> => (w, z) => complex_binary(dep.add(w.re, z.re), dep.add(w.im, z.im)) -export const addReal = - (dep: Dependency<'addReal', [T, UnderlyingReal]>): - ImpType<'addReal', [Complex, UnderlyingReal]> => - (z, r) => complex_binary(dep.addReal(z.re, r), z.im) +export const add_real = + (dep: Dependency<'add_real', [T, UnderlyingReal]>): + ImpType<'add_real', [Complex, UnderlyingReal]> => + (z, r) => complex_binary(dep.add_real(z.re, r), z.im) export const unaryMinus = (dep: Dependency<'unaryMinus', [T]>): @@ -73,22 +71,22 @@ export const multiply = export const absquare = (dep: Dependency<'absquare', [T]> - & Dependency<'add', BBinary>>): + & Dependency<'add', [UnderlyingReal, UnderlyingReal]>): ImpType<'absquare', [Complex]> => z => dep.add(dep.absquare(z.re), dep.absquare(z.im)) -export const divideByReal = - (dep: Dependency<'divideByReal', [T, UnderlyingReal]>): - ImpType<'divideByReal', [Complex, UnderlyingReal]> => +export const divide_real = + (dep: Dependency<'divide_real', [T, UnderlyingReal]>): + ImpType<'divide_real', [Complex, UnderlyingReal]> => (z, r) => complex_binary( - dep.divideByReal(z.re, r), dep.divideByReal(z.im, r)) + dep.divide_real(z.re, r), dep.divide_real(z.im, r)) export const reciprocal = (dep: Dependency<'conj', [Complex]> & Dependency<'absquare', [Complex]> - & Dependency<'divideByReal', [Complex, UnderlyingReal]>): + & Dependency<'divide_real', [Complex, UnderlyingReal]>): ImpType<'reciprocal', [Complex]> => - z => dep.divideByReal(dep.conj(z), dep.absquare(z)) + z => dep.divide_real(dep.conj(z), dep.absquare(z)) export const divide = (dep: Dependency<'multiply', [Complex, Complex]> @@ -96,44 +94,31 @@ export const divide = ImpType<'divide', [Complex, Complex]> => (w, z) => dep.multiply(w, dep.reciprocal(z)) -export const complexSqrt = - (dep: Dependency<'conservativeSqrt', [T]> - & Dependency<'isSquare', [T]> - & Dependency<'complex', [T]> - & Dependency<'unaryMinus', [T]> - & Dependency<'zero', [T]> - & Dependency<'nan', [Complex]>): ImpType<'complexSqrt', [T]> => - r => { - if (dep.isSquare(r)) return dep.complex(dep.conservativeSqrt(r)) - const negative = dep.unaryMinus(r) - if (dep.isSquare(negative)) { - return complex_binary( - dep.zero(r), dep.conservativeSqrt(negative)) - } - // neither the real number or its negative is a square; could happen - // for example with bigint. So there is no square root. So we have to - // return the NaN of the type. - return dep.nan(dep.complex(r)) - } - export const sqrt = - (dep: Dependency<'isReal', [Complex]> - & Dependency<'complexSqrt', [T]> - & Dependency<'absquare', [Complex]> - & Dependency<'conservativeSqrt', [UnderlyingReal]> - & Dependency<'addReal', [Complex,UnderlyingReal]> - & Dependency<'re', [Complex]> - & Dependency<'add', [UnderlyingReal,UnderlyingReal]> - & Dependency<'divideByReal', [Complex,UnderlyingReal]> + (dep: Dependency<'absquare' | 're', [Complex]> + & Dependency<'conservativeSqrt' | 'unaryMinus', [UnderlyingReal]> + & Dependency<'divide_real', [Complex, UnderlyingReal]> + & Dependency<'add_real', [T, UnderlyingReal]> + & {add_complex_real: + ImpType<'add_real', [Complex, UnderlyingReal]>} + & Dependency<'equal' | 'add', [UnderlyingReal, UnderlyingReal]> + & Dependency<'complex', [T, T]> + & Dependency<'zero', [T]> ): ImpType<'sqrt', [Complex]> => z => { - if (dep.isReal(z)) return dep.complexSqrt(z.re) const myabs = dep.conservativeSqrt(dep.absquare(z)) - const num = dep.addReal(z, myabs) const r = dep.re(z) + const negr = dep.unaryMinus(r) + if (dep.equal(myabs, negr)) { + // pure imaginary square root; z.im already zero + return dep.complex( + dep.zero(z.re), dep.add_real(z.im, dep.conservativeSqrt(negr))) + } + const num = dep.add_complex_real(z, myabs) const denomsq = dep.add(dep.add(myabs, myabs), dep.add(r, r)) const denom = dep.conservativeSqrt(denomsq) - return dep.divideByReal(num, denom) + return dep.divide_real(num, denom) } + export const conservativeSqrt = sqrt diff --git a/src/Complex/native.ts b/src/Complex/native.ts index 6a91ee7..ea2f66b 100644 --- a/src/Complex/native.ts +++ b/src/Complex/native.ts @@ -1 +1,4 @@ export * from './type.js' +export * from './arithmetic.js' +export * from './predicate.js' +export * from './relational.js' diff --git a/src/Complex/predicate.ts b/src/Complex/predicate.ts index eafc5ad..c4b204e 100644 --- a/src/Complex/predicate.ts +++ b/src/Complex/predicate.ts @@ -1,10 +1,12 @@ import {Complex} from './type.js' -import {Signature, Dependency, ImpType} from '../core/Dispatcher.js' +import {Dependency, ImpType} from '../core/Dispatcher.js' + +type ComplexPredicate = T extends Complex ? (a: T) => boolean : never declare module "./type" { - interface ComplexReturn { - isReal: Signature], boolean> - isSquare: Signature], boolean> + interface ComplexImpTypes { + isReal: ComplexPredicate + isSquare: ComplexPredicate } } diff --git a/src/Complex/relational.ts b/src/Complex/relational.ts index 2a57dc4..ac2d1b4 100644 --- a/src/Complex/relational.ts +++ b/src/Complex/relational.ts @@ -1,11 +1,12 @@ import {Complex} from './type.js' -import {BBinary, ImpType, Dependency} from '../core/Dispatcher.js' +import {ImpType, Dependency} from '../core/Dispatcher.js' + +type ComplexRelation = + T extends Complex ? (a: T, b: T) => boolean : never declare module "./type" { - interface ComplexReturn { - equal: Params extends BBinary - ? B extends Complex ? boolean : never - : never + interface ComplexImpTypes { + equal: ComplexRelation } } diff --git a/src/Complex/type.ts b/src/Complex/type.ts index 36c040a..d8ccac2 100644 --- a/src/Complex/type.ts +++ b/src/Complex/type.ts @@ -1,5 +1,5 @@ import { - joinTypes, typeOfDependency, Dependency, BBinary, ImpType, ImpReturns + joinTypes, typeOfDependency, Dependency, ImpType, ImpReturns } from '../core/Dispatcher.js' export type Complex = {re: T; im: T;} @@ -22,45 +22,16 @@ export const Complex_type = { } } -export interface ComplexReturn { - // 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 // unary case - : Params extends BBinary ? Complex // 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 : never - // complex_binary: Params extends BBinary ? Complex : 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 : 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 // 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 // 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 // of a Complex parameter - ? ImpReturns<'nan', T> extends T ? Z : never // has real NaN - : never - : never - re: Params extends [infer Z] - ? Z extends Complex ? UnderlyingReal : never - : never +export interface ComplexImpTypes { + complex: (a: T, b?: T) => Complex + zero: T extends Complex + ? (a: Complex) => Complex> : never + one: T extends Complex + ? (a: Complex) => Complex> : never + nan: T extends Complex + ? (a: Complex) => Complex> : never + re: T extends Complex + ? (a: Complex) => UnderlyingReal : never } export const complex_unary = diff --git a/src/core/Dispatcher.ts b/src/core/Dispatcher.ts index aa4c6a9..c1167f1 100644 --- a/src/core/Dispatcher.ts +++ b/src/core/Dispatcher.ts @@ -15,22 +15,22 @@ type DependenciesType = Record export type typeOfDependency = {typeOf: (x: unknown) => TypeName} // All of the implementations must publish descriptions of their -// return types into the following interface, using the format +// types into the following interface, using the format // described just below: -export interface ReturnTypes {} +export interface ImpTypes {} /***** To describe one implementation for a hypothetical operation `foo`, there should be a property of the interface whose name starts with `foo` and whose next character, if any, is an underscore. The type of this property - must be the return type of that implementation when Params matches the - parameter types of the implementation, and `never` otherwise. + must be the type of that implementation when T matches the + first parameter of the implementation. Thus to describe an implementation that takes a number and a string and returns a boolean, for example, you could write ``` declare module "Dispatcher" { - interface ReturnTypes { - foo_example: Params extends [number, string] ? boolean : never + interface ImpTypes { + foo_example: (a: number, b: string) => boolean } } ``` @@ -38,52 +38,26 @@ export interface ReturnTypes {} of any type and returns a Vector of that type, you can say ``` ... - foo_generic: Params extends [infer T] ? Vector : never + foo_generic: (a: T) => Vector ... ``` In practice, each subdirectory corresponding to a type, like Complex, - defines an interface, like `ComplexReturn` for the implementations + defines an interface, like `ComplexImpTypes` for the implementations in that subdirectory, which can mostly be defined without suffixes because there's typically just a single implementation within that domain. Then the module responsible for collating all of the implementations for that type inserts all of the properties of that interface into `ReturnTypes` suitably suffixed to avoid collisions. - One might think that simply defining an implementation for `foo` - of type `(n: number, s: string) => boolean` would provide all of the same - information as the type of the key `foo_example` in the ReturnTypes - interface above, but in practice TypeScript has challenges in extracting - types relating to functions. (In particular, there is no - way to get the specialized return type of a generic function when it is - called on aguments whose specific types match the generic parameters.) - Hence the need for this additional mechanism to specify return types, in - a way readily suited for TypeScript type computations. + Note that if the type is not generic, it will not bother with the generic + parameter in its subdirectory ImpTypes interface. + + And note again, that for generic types, the type parameter of ImpTypes + _must_ match the type of the **first** argument of the operation. Hence, + choose argument order so that you can infer all the other parameter types + and the return type from the type of the first argument. *****/ -// Helpers for specifying signatures - -// A basic signature with concrete types -export type Signature = - 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 ? B : never` -// says that this implementation takes two arguments, both of type B, and -// returns the same type. -export type BBinary = [B, B] - -// A unary signature that preserves the type of its argument, which must -// extend the given Bound: -export type ConservativeUnary = - 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 extends BBinary - ? 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 = keyof IFace extends string @@ -99,16 +73,17 @@ export function joinTypes(a: TypeName, b: TypeName) { // Used to filter keys that match a given operation name type BeginsWith = Name | `${Name}_${string}` -// Look up the return type of an implementation based on its name -// and the parameters it takes -export type ImpReturns = - {[K in keyof ReturnTypes]: K extends BeginsWith - ? ReturnTypes[K] : never}[keyof ReturnTypes] +export type RawDependency = + {[K in keyof ImpTypes]: K extends BeginsWith + ? ImpTypes[K] extends (...args: Params) => any + ? ImpTypes[K] + : never + : never} // The type of an implementation (with dependencies satisfied, // based on its name and the parameters it takes export type ImpType = - (...args: Params) => ImpReturns + RawDependency[keyof ImpTypes] // The type of a dependency on an implementation based on its name // and the parameters it takes (just a simple object with one property @@ -118,6 +93,12 @@ export type ImpType = export type Dependency = {[N in Name]: ImpType} +// Look up the return type of an implementation based on its name +// and the parameters it takes +export type ImpReturns = + ReturnType> + + // Now types used in the Dispatcher class itself type TypeSpecification = { diff --git a/src/generic/all.ts b/src/generic/all.ts index 1a008ac..7548c94 100644 --- a/src/generic/all.ts +++ b/src/generic/all.ts @@ -1,10 +1,10 @@ import { ForType } from '../core/Dispatcher.js' -import { GenericReturn } from './type.js' -import * as generic from './arithmetic.js' +import { GenericImpTypes } from './type.js' +import * as generic from './native.js' export { generic } declare module "../core/Dispatcher" { - interface ReturnTypes - extends ForType<'generic', GenericReturn> { } + interface ImpTypes + extends ForType<'generic', GenericImpTypes> { } } diff --git a/src/generic/arithmetic.ts b/src/generic/arithmetic.ts index 46d6922..cc44deb 100644 --- a/src/generic/arithmetic.ts +++ b/src/generic/arithmetic.ts @@ -1,39 +1,12 @@ -import {Dependency, ImpType, ImpReturns} from "../core/Dispatcher"; +import {Dependency, ImpType, ImpReturns} from '../core/Dispatcher.js' declare module "./type" { - interface GenericReturn { - // Jos: not sure how to define this or why it is needed - // square: Signature - // square: ConservativeUnary - // square: Params extends [infer R] - // ? R extends number ? UnderlyingReal : 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). + interface GenericImpTypes { + square: (a: T) => ImpReturns<'multiply', [T, T]> } } export const square = (dep: Dependency<'multiply', [T, T]>): ImpType<'square', [T]> => - z => dep.multiply(z, z) + t => dep.multiply(t, t) diff --git a/src/generic/type.ts b/src/generic/type.ts index 8589417..7b7f208 100644 --- a/src/generic/type.ts +++ b/src/generic/type.ts @@ -1,3 +1,3 @@ -export interface GenericReturn { +export interface GenericImpTypes { -} \ No newline at end of file +} diff --git a/src/index.ts b/src/index.ts index bb83486..297b271 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,3 +2,19 @@ import {Dispatcher} from './core/Dispatcher.js' import * as Specifications from './all.js' export default new Dispatcher(Specifications) + +import {Complex} from './Complex/type.js' +import {absquare as absquare_complex} from './Complex/arithmetic.js' + +const mockRealAdd = (a: number, b: number) => a+b +const mockComplexAbsquare = (z: Complex) => z.re*z.re + z.im*z.im + +const quatAbsquare = absquare_complex({ + add: mockRealAdd, + absquare: mockComplexAbsquare +}) + +const myabs = quatAbsquare({re: {re: 0, im: 1}, im: {re:2, im: 3}}) +const typeTest: typeof myabs = 7 // check myabs is just a number + +console.log('Result is', myabs) diff --git a/src/numbers/all.ts b/src/numbers/all.ts index b034f25..74fb578 100644 --- a/src/numbers/all.ts +++ b/src/numbers/all.ts @@ -1,10 +1,10 @@ import {ForType} from '../core/Dispatcher.js' -import {NumbersReturn} from './type.js' +import {NumbersImpTypes} from './type.js' import * as numbers from './native.js' export {numbers} declare module "../core/Dispatcher" { - interface ReturnTypes - extends ForType<'numbers', NumbersReturn> {} + interface ImpTypes + extends ForType<'numbers', NumbersImpTypes> {} } diff --git a/src/numbers/arithmetic.ts b/src/numbers/arithmetic.ts index b02b09f..a2b7fb1 100644 --- a/src/numbers/arithmetic.ts +++ b/src/numbers/arithmetic.ts @@ -1,53 +1,32 @@ import {configDependency} from '../core/Config.js' import { - Signature, ConservativeBinary, ConservativeUnary, Dependency, ImpType + Dependency, ImpType } from '../core/Dispatcher.js' import type {Complex, UnderlyingReal} from '../Complex/type.js' +type UnaryNumber = (a: number) => number +type BinaryNumber = (a: number, b:number) => number + declare module "./type" { - interface NumbersReturn { - // 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 - - // 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 - // 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 - conj: ConservativeUnary - subtract: ConservativeBinary - multiply: ConservativeBinary - absquare: Params extends [infer R] - ? R extends number ? UnderlyingReal : never - : never - reciprocal: ConservativeUnary - divide: ConservativeBinary - 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 + interface NumbersImpTypes { + add: BinaryNumber + unaryMinus: UnaryNumber + conj: UnaryNumber + subtract: BinaryNumber + multiply: BinaryNumber + absquare: UnaryNumber + reciprocal: UnaryNumber + divide: BinaryNumber + conservativeSqrt: UnaryNumber // 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> + sqrt: (a: number) => number | Complex } } export const add: ImpType<'add', [number, number]> = (a, b) => a + b -export const addReal = add export const unaryMinus: ImpType<'unaryMinus', [number]> = a => -a export const conj: ImpType<'conj', [number]> = a => a export const subtract: ImpType<'subtract', [number, number]> = (a, b) => a - b @@ -55,7 +34,6 @@ export const multiply: ImpType<'multiply', [number, number]> = (a, b) => a * b export const absquare: ImpType<'absquare', [number]> = a => a*a export const reciprocal: ImpType<'reciprocal', [number]> = a => 1/a export const divide: ImpType<'divide', [number, number]> = (a, b) => a / b -export const divideByReal: ImpType<'divideByReal', [number, number]> = divide export const conservativeSqrt: ImpType<'conservativeSqrt', [number]> = a => isNaN(a) ? NaN : Math.sqrt(a) diff --git a/src/numbers/native.ts b/src/numbers/native.ts index 10cd111..ea2f66b 100644 --- a/src/numbers/native.ts +++ b/src/numbers/native.ts @@ -1,2 +1,4 @@ export * from './type.js' export * from './arithmetic.js' +export * from './predicate.js' +export * from './relational.js' diff --git a/src/numbers/predicate.ts b/src/numbers/predicate.ts index b8cc4c5..5d6ac84 100644 --- a/src/numbers/predicate.ts +++ b/src/numbers/predicate.ts @@ -1,9 +1,10 @@ -import {Signature, ImpType} from '../core/Dispatcher.js' +import {ImpType} from '../core/Dispatcher.js' +type NumberPredicate = (a: number) => boolean declare module "./type" { - interface NumbersReturn { - isReal: Signature - isSquare: Signature + interface NumbersImpTypes { + isReal: NumberPredicate + isSquare: NumberPredicate } } diff --git a/src/numbers/relational.ts b/src/numbers/relational.ts index 51d7e07..b7ce71a 100644 --- a/src/numbers/relational.ts +++ b/src/numbers/relational.ts @@ -1,12 +1,13 @@ import {configDependency} from '../core/Config.js' -import {Signature, ImpType, Dependency} from '../core/Dispatcher.js' +import {ImpType, Dependency} from '../core/Dispatcher.js' const DBL_EPSILON = Number.EPSILON || 2.2204460492503130808472633361816E-16 +type NumberRelation = (a: number, b: number) => boolean + declare module "./type" { - interface NumbersReturn { - equal: Signature - unequal: Signature + interface NumbersImpTypes { + equal: NumberRelation } } @@ -26,9 +27,3 @@ export const equal = return false } - -export const unequal = (dep: Dependency<'equal', [number, number]>): - ImpType<'unequal', [number, number]> => - (x, y) => { - return !dep.equal(x, y) - } diff --git a/src/numbers/type.ts b/src/numbers/type.ts index 46971fc..a2db62d 100644 --- a/src/numbers/type.ts +++ b/src/numbers/type.ts @@ -8,34 +8,11 @@ export const number_type = { } -export interface NumbersReturn { - // The following description of the return type of `zero` on a single - // number argument has ended up unfortunately rather complicated. However, - // 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 - // makes complex fail to compile, because it worries that you might be - // making `Complex` 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 : never - : never +export interface NumbersImpTypes { + zero: (a: number) => 0 + one: (a: number) => 1 + nan: (a: number) => typeof NaN + re: (a: number) => number } export const zero: ImpType<'zero', [number]> = a => 0