feat: TypeScript typings for Dispatcher implementations

A first pass at specifying some implementations in TypeScript
  that actually compiles. It doesn't do anything, as installing
  types and operation specifications are currently dummy operations,
  but they are all invoked.
This commit is contained in:
Glen Whitney 2022-12-06 20:21:05 -05:00
parent 2a9039ac67
commit ccc6153786
14 changed files with 100 additions and 123 deletions

1
src/Complex/all.ts Normal file
View File

@ -0,0 +1 @@
export * as Complex from './native.js'

1
src/Complex/native.ts Normal file
View File

@ -0,0 +1 @@
export * from './type.js'

30
src/Complex/type.ts Normal file
View File

@ -0,0 +1,30 @@
/// <reference path="../numbers/type.ts">
import {joinTypes, typeOfDependency, Dependency} from '../core/Dispatcher.js'
export type Complex<T> = {re: T; im: T;}
export const Complex_type = {
test: <T>(dep: {testT: (z: unknown) => z is T}) =>
(z: unknown): z is Complex<T> =>
typeof z === 'object' && 're' in z && 'im' in z
&& dep.testT(z.re) && dep.testT(z.im),
infer: (dep: typeOfDependency) =>
(z: Complex<unknown>) => joinTypes(dep.typeOf(z.re), dep.typeOf(z.im)),
from: {
T: <T>(dep: Dependency<'zero', [T]>) => (t: T) =>
({re: t, im: dep.zero(t)}),
Complex: <U,T>(dep: {convert: (from: U) => T}) =>
(z: Complex<U>) => ({re: dep.convert(z.re), im: dep.convert(z.im)})
}
}
export const complex_1 = <T>(dep: Dependency<'zero', [T]>) =>
(t: T) => ({re: t, im: dep.zero(t)})
export const complex_2 = <T>(t: T, u: T) => ({re: t, im: u})
declare module "../core/Dispatcher" {
interface ImplementationTypes {
complex_1_Complex: typeof complex_1
complex_2_Complex: typeof complex_2
}
}

2
src/all.ts Normal file
View File

@ -0,0 +1,2 @@
export * from './numbers/all.js'
export * from './Complex/all.js'

View File

@ -1,33 +0,0 @@
import {Specifications, joinTypes, typeOfDependency} from '../core/Dispatcher'
export type Complex<T> = {re: T; im: T;}
declare module 'Dispatcher' {
namespace Specifications {
export class ComplexSpecifications {}
namespace ComplexSpecifications {
export class Complex_type<T> {
static test = (testT: (z: unknown) => z is T) =>
(z: unknown): z is Complex<T> =>
typeof z === 'object'
&& 're' in z && 'im' in z
&& testT(z.re) && testT(z.im);
static infer = (dep: typeOfDependency) =>
(z: Complex<unknown>) =>
joinTypes(dep.typeOf(z.re), dep.typeOf(z.im));
static from = {
T: (dep: ImplementationType<'zero', [T]>) => (t: T) =>
({re: t, im: dep.zero(t)}),
Complex: <U>(convert: (from: U) => T) =>
(z: Complex<U>) => ({re: convert(z.re), im: convert(z.im)})
};
}
export const complex_1 = <T>(dep: DependencyType<'zero', [T]>) =>
(t: T) => ({re: t, im: dep.zero(t)})
export const complex_2 = <T>(t: T, u: T) => ({re: t, im: u})
}
}
}
export {Specifications}

View File

@ -2,6 +2,6 @@ export type Config = {
predictable: boolean
}
export type ConfigDependency = {
export type configDependency = {
config: Config
}

View File

@ -5,11 +5,13 @@
* for specific types (including their own).
*/
// First helper types and functions for the Dispatcher
type TypeName = string
type Parameter = TypeName
type Signature = Parameter[]
export class Specifications {}
export interface ImplementationTypes {}
export type typeOfDependency = {typeOf: (x: unknown) => TypeName}
//dummy implementation for now
@ -18,64 +20,46 @@ export function joinTypes(a: TypeName, b: TypeName) {
return 'any'
}
// Will need to build this up. Need to start with looping through the keys of
// values of keys, and filtering ones that start with Name, then add in
// checking the types.
// Some relevant stuff that worked in the playground:
// type KeysMatching<T, N, V> = {[K in keyof T]-?: T[K] extends V ? K extends `${string}${N}${string}` ? K : never : never}[keyof T];
// type ValuesMatching<T, V> = {[K in keyof T]: T[K] extends V ? T[K] : never}[keyof T]
// type SubKeysMatching<T, N, V> = {[K in keyof T]: KeysMatching<T[K], N, V>}[keyof T]
// type SubValuesMatching<T, V> = {[K in keyof T]: ValuesMatching<T[K], V>}[keyof T]
// let trial: SubKeysMatching<typeof Bar, 'ng', number | boolean> = 'strange'
// let valtrial: SubValuesMatching<typeof Bar, number> = 3
// type MyFunc = (...args: [string, number]) => any
// Selecting the proper key for arguments [string, number] is working
// let key: KeysMatching<typeof Foo, 'a', MyFunc > = 'bar' // OK, and 'baz' here does fail, as desired
// The above should have all of the ingredients.
type DependenciesType = Record<String, Function>
/**
* Build up to Dependency type lookup
*/
type DependenciesType = Record<string, Function>
type FinalShape<FuncType> =
FuncType extends (arg: DependenciesType) => Function
? ReturnType<FuncType> : FuncType
type BeginsWith<Name> = `${Name}${string}`
type BeginsWith<Name extends string> = `${Name}${string}`
type ImmediateDependency<Ob, Name, ParamTuple> =
type DependencyTypes<Ob, Name extends string, Params extends unknown[]> =
{[K in keyof Ob]: K extends BeginsWith<Name>
? FinalShape<Ob[K]> extends (...args: ParamTuple) => any
? FinalShape<Ob[K]> extends (...args: Params) => any
? FinalShape<Ob[K]>
: never
: never}[keyof Ob]
: never}
export type Dependency<Name extends string, Params extends unknown[]> =
{[N in Name]:
DependencyTypes<ImplementationTypes, N, Params>[keyof ImplementationTypes]}
type SpecType = typeof Specifications
export type ImplementationDependency<Name, ParamTuple> =
{[S in keyof SpecType]:
ImmediateDependency<SpecType[S], Name, ParamTuple>}[keyof SpecType]
// Now types used in the Dispatcher class itself
type TypeSpecification = {
before?: TypeName[],
test: ((x: unknown) => boolean)
| ((d: DependenciesType) => (x: unknown) => boolean),
from: Record<Typename, Function>,
from: Record<TypeName, Function>,
infer?: (d: DependenciesType) => (z: unknown) => TypeName
}
type SpecObject = Record<string, Function | TypeSpecification>
export type SpecifcationsGroup = Record<string, SpecObject>
type SpecificationsGroup = Record<string, SpecObject>
export class Dispatcher {
installSpecification(
name: string,
signature: Signature,
returns: Type,
returns: TypeName,
dependencies: Record<string, Signature>,
behavior: Function // possible todo: constrain this type based
// on the signature, return type, and dependencies. Not sure if
@ -89,19 +73,21 @@ export class Dispatcher {
//TODO: implement me
}
constructor(collection: SpecificationsGroup) {
for (key in collection) {
for (const key in collection) {
console.log('Working on', key)
for (identifier in collection[key]) {
for (const identifier in collection[key]) {
console.log('Handling', key, ':', identifier)
const parts = identifier.split('_')
if (parts[parts.length - 1] === 'type') {
parts.pop()
const name = parts.join('_')
installType(name, collection[key][identifier])
this.installType(
name, collection[key][identifier] as TypeSpecification)
} else {
const name = parts[0]
installSpecification(
name, ['dunno'], 'unsure', {}, collection[key][identifier])
this.installSpecification(
name, ['dunno'], 'unsure', {},
collection[key][identifier] as Function)
}
}
}

View File

@ -1,5 +1,4 @@
import Dispatcher from 'core/Dispatcher'
import Complex from 'complex/type'
import Specifications from 'number/arithmetic'
import {Dispatcher} from './core/Dispatcher.js'
import * as Specifications from './all.js'
export default new Dispatcher(Specifications)

View File

@ -1,29 +0,0 @@
import Specifications from './type'
import configDependency from '../core/Config'
/// <reference path="../complex/type.ts">
declare module 'Dispatcher' {
namespace Specifications {
namespace NumberSpecifications {
export const add = (a: number, b: number) => a + b
export const unaryMinus = (a: number) => -a
export const subtract = (a: number, b: number) => a - b
export const multiply = (a: number, b: number) => a * b
export const divide = (a: number, b: number) => a / b
export const sqrt =
(dep: configDependency
& ImplementationDependency<'complex', [number,number]>) => {
if (dep.config.predictable || !dep.complex) {
return (a: number) => isNaN(n) ? NaN : Math.sqrt(n)
}
return (a: number) => {
if (isNaN(n)) return NaN
if (n >= 0) return Math.sqrt(n)
return dep.complex(0, Math.sqrt(unaryMinus(n)))
}
}
}
}
}
export {Specifications}

View File

@ -1,17 +0,0 @@
import Specifications from '../core/Dispatcher'
declare module 'Dispatcher' {
namespace Specifications {
export class NumberSpecifications {}
namespace NumberSpecifications {
export const number_type = {
before: ['Complex'],
test: (n: unknown): n is number => typeof n === 'number',
from: {string: s => +s}
}
export const zero = (a: number) => 0
}
}
}
export {Specifications}

1
src/numbers/all.ts Normal file
View File

@ -0,0 +1 @@
export * as numbers from './native.js'

21
src/numbers/arithmetic.ts Normal file
View File

@ -0,0 +1,21 @@
/// <reference path="../Complex/type.ts">
import {configDependency} from '../core/Config.js'
import {Dependency, ImplementationTypes} from '../core/Dispatcher.js'
export const add = (a: number, b: number) => a + b
export const unaryMinus = (a: number) => -a
export const subtract = (a: number, b: number) => a - b
export const multiply = (a: number, b: number) => a * b
export const divide = (a: number, b: number) => a / b
export const sqrt =
(dep: configDependency
& Dependency<'complex', [number, number]>) => {
if (dep.config.predictable || !dep.complex) {
return (a: number) => isNaN(a) ? NaN : Math.sqrt(a)
}
return (a: number) => {
if (isNaN(a)) return NaN
if (a >= 0) return Math.sqrt(a)
return dep.complex(0, Math.sqrt(unaryMinus(a)))
}
}

2
src/numbers/native.ts Normal file
View File

@ -0,0 +1,2 @@
export * from './type.js'
export * from './arithmetic.js'

13
src/numbers/type.ts Normal file
View File

@ -0,0 +1,13 @@
export const number_type = {
before: ['Complex'],
test: (n: unknown): n is number => typeof n === 'number',
from: {string: s => +s}
}
export const zero = (a: number) => 0
declare module "../core/Dispatcher" {
interface ImplementationTypes {
zero_numbers: typeof zero
}
}