refactor: Convenience type operator for specifying concrete signatures

This commit is contained in:
Glen Whitney 2022-12-21 11:41:25 -05:00
parent 1eb73be2fa
commit d55776655f
4 changed files with 22 additions and 13 deletions

View File

@ -19,9 +19,10 @@ export const Complex_type = {
} }
} }
type Binary<B> = [B, B]
export interface ComplexReturn<Params> { 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 complex: Params extends [infer U] ? Complex<U> // unary case
: Params extends BBinary<infer B> ? Complex<B> // binary case : Params extends BBinary<infer B> ? Complex<B> // binary case
: never : never

View File

@ -9,7 +9,7 @@
type TypeName = string type TypeName = string
type Parameter = TypeName type Parameter = TypeName
type Signature = Parameter[] type InputSignature = Parameter[]
type DependenciesType = Record<string, Function> type DependenciesType = Record<string, Function>
export type typeOfDependency = {typeOf: (x: unknown) => TypeName} export type typeOfDependency = {typeOf: (x: unknown) => TypeName}
@ -62,7 +62,11 @@ export interface ReturnTypes<Params> {}
// Helpers for specifying signatures // Helpers for specifying signatures
// A homogenous binary operation (comes up a lot) // A basic signature with concrete types
export type Signature<CandidateParams, ActualParams, Returns> =
CandidateParams extends ActualParams ? Returns : never
// A homogenous binary operation (comes up a lot, needs a better name?)
// Typical usage: `foo_impl: Params extends BBinary<infer B> ? B : never` // Typical usage: `foo_impl: Params extends BBinary<infer B> ? B : never`
// says that this implementation takes two arguments, both of type B, and // says that this implementation takes two arguments, both of type B, and
// returns the same type. // returns the same type.
@ -118,9 +122,9 @@ type SpecificationsGroup = Record<string, SpecObject>
export class Dispatcher { export class Dispatcher {
installSpecification( installSpecification(
name: string, name: string,
signature: Signature, signature: InputSignature,
returns: TypeName, returns: TypeName,
dependencies: Record<string, Signature>, dependencies: Record<string, InputSignature>,
behavior: Function // possible todo: constrain this type based behavior: Function // possible todo: constrain this type based
// on the signature, return type, and dependencies. Not sure if // on the signature, return type, and dependencies. Not sure if
// that's really possible, though. // that's really possible, though.

View File

@ -1,5 +1,5 @@
import {configDependency} from '../core/Config.js' import {configDependency} from '../core/Config.js'
import {BBinary, Dependency, ImpType} from '../core/Dispatcher.js' import {Signature, Dependency, ImpType} from '../core/Dispatcher.js'
import type {Complex} from '../Complex/type.js' import type {Complex} from '../Complex/type.js'
declare module "./type" { declare module "./type" {
@ -7,7 +7,7 @@ declare module "./type" {
// This description loses information: some subtypes like NumInt or // This description loses information: some subtypes like NumInt or
// Positive are closed under addition, but this says that the result // Positive are closed under addition, but this says that the result
// of add is just a number, not still of the reduced type // of add is just a number, not still of the reduced type
add: Params extends BBinary<number> ? number : never add: Signature<Params, [number, number], number>
// Whereas this one would preserve information, but would lie // Whereas this one would preserve information, but would lie
// because it claims all subtypes of number are closed under addition, // because it claims all subtypes of number are closed under addition,
// which is not true for `1 | 2 | 3`, for example. // which is not true for `1 | 2 | 3`, for example.
@ -16,15 +16,15 @@ declare module "./type" {
// : never // : never
// //
// Not sure how this will need to go when we introduce NumInt. // Not sure how this will need to go when we introduce NumInt.
unaryMinus: Params extends [number] ? number : never unaryMinus: Signature<Params, [number], number>
subtract: Params extends BBinary<number> ? number : never subtract: Signature<Params, [number, number], number>
multiply: Params extends BBinary<number> ? number : never multiply: Signature<Params, [number, number], number>
divide: Params extends BBinary<number> ? number : never divide: Signature<Params, [number, number], number>
// Best we can do for sqrt at compile time, since actual return // Best we can do for sqrt at compile time, since actual return
// type depends on config. Not sure how this will play out // type depends on config. Not sure how this will play out
// when we make a number-only bundle, but at least the import type // when we make a number-only bundle, but at least the import type
// above for Complex<> does not lead to any emitted JavaScript. // above for Complex<> does not lead to any emitted JavaScript.
sqrt: Params extends [number] ? (number | Complex<number>) : never sqrt: Signature<Params, [number], number | Complex<number>>
} }
} }

View File

@ -21,6 +21,10 @@ export interface NumbersReturn<Params> {
zero: Params extends [infer T] zero: Params extends [infer T]
? T extends number ? 0 extends T ? 0 : never : never ? T extends number ? 0 extends T ? 0 : never : 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.
} }
export const zero: ImpType<'zero', [number]> = a => 0 export const zero: ImpType<'zero', [number]> = a => 0