diff --git a/.gitignore b/.gitignore index cf4f3ae..0dc139f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,9 @@ # Emacs backups *~ +# Typescript +# emitted code +obj + # ---> Node # Logs logs diff --git a/src/complex/type.ts b/src/complex/type.ts new file mode 100644 index 0000000..d41c06b --- /dev/null +++ b/src/complex/type.ts @@ -0,0 +1,33 @@ +import {Specifications, joinTypes, typeOfDependency} from '../core/Dispatcher' + +export type Complex = {re: T; im: T;} + +declare module 'Dispatcher' { + namespace Specifications { + export class ComplexSpecifications {} + + namespace ComplexSpecifications { + export class Complex_type { + static test = (testT: (z: unknown) => z is T) => + (z: unknown): z is Complex => + typeof z === 'object' + && 're' in z && 'im' in z + && testT(z.re) && testT(z.im); + static infer = (dep: typeOfDependency) => + (z: Complex) => + 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: (convert: (from: U) => T) => + (z: Complex) => ({re: convert(z.re), im: convert(z.im)}) + }; + } + export const complex_1 = (dep: DependencyType<'zero', [T]>) => + (t: T) => ({re: t, im: dep.zero(t)}) + export const complex_2 = (t: T, u: T) => ({re: t, im: u}) + } + } +} + +export {Specifications} diff --git a/src/core/Config.ts b/src/core/Config.ts new file mode 100644 index 0000000..6ae5e58 --- /dev/null +++ b/src/core/Config.ts @@ -0,0 +1,7 @@ +export type Config = { + predictable: boolean +} + +export type ConfigDependency = { + config: Config +} diff --git a/src/core/Dispatcher.ts b/src/core/Dispatcher.ts new file mode 100644 index 0000000..0d05933 --- /dev/null +++ b/src/core/Dispatcher.ts @@ -0,0 +1,109 @@ +/* A Dispatcher is a collection of operations that do run-time + * dispatch on the types of their arguments. Thus, every individual + * method is like a typed-function (from the library by that name), + * but they can depend on one another and on ona another's implementations + * for specific types (including their own). + */ + +type TypeName = string +type Parameter = TypeName +type Signature = Parameter[] + +export class Specifications {} +export type typeOfDependency = {typeOf: (x: unknown) => TypeName} + +//dummy implementation for now +export function joinTypes(a: TypeName, b: TypeName) { + if (a === b) return a + 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 = {[K in keyof T]-?: T[K] extends V ? K extends `${string}${N}${string}` ? K : never : never}[keyof T]; +// type ValuesMatching = {[K in keyof T]: T[K] extends V ? T[K] : never}[keyof T] + +// type SubKeysMatching = {[K in keyof T]: KeysMatching}[keyof T] +// type SubValuesMatching = {[K in keyof T]: ValuesMatching}[keyof T] + + +// let trial: SubKeysMatching = 'strange' +// let valtrial: SubValuesMatching = 3 + +// type MyFunc = (...args: [string, number]) => any + +// Selecting the proper key for arguments [string, number] is working +// let key: KeysMatching = 'bar' // OK, and 'baz' here does fail, as desired + +// The above should have all of the ingredients. +type DependenciesType = Record + +type FinalShape = + FuncType extends (arg: DependenciesType) => Function + ? ReturnType : FuncType + +type BeginsWith = `${Name}${string}` + +type ImmediateDependency = + {[K in keyof Ob]: K extends BeginsWith + ? FinalShape extends (...args: ParamTuple) => any + ? FinalShape + : never + : never}[keyof Ob] + +type SpecType = typeof Specifications + +export type ImplementationDependency = + {[S in keyof SpecType]: + ImmediateDependency}[keyof SpecType] + +type TypeSpecification = { + before?: TypeName[], + test: ((x: unknown) => boolean) + | ((d: DependenciesType) => (x: unknown) => boolean), + from: Record, + infer?: (d: DependenciesType) => (z: unknown) => TypeName +} + +type SpecObject = Record +export type SpecifcationsGroup = Record + +export class Dispatcher { + installSpecification( + name: string, + signature: Signature, + returns: Type, + dependencies: Record, + behavior: Function // possible todo: constrain this type based + // on the signature, return type, and dependencies. Not sure if + // that's really possible, though. + ) { + console.log('Pretending to install', name, signature, '=>', returns) + //TODO: implement me + } + installType(name: TypeName, typespec: TypeSpecification) { + console.log('Pretending to install type', name, typespec) + //TODO: implement me + } + constructor(collection: SpecificationsGroup) { + for (key in collection) { + console.log('Working on', key) + for (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]) + } else { + const name = parts[0] + installSpecification( + name, ['dunno'], 'unsure', {}, collection[key][identifier]) + } + } + } + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..3df603f --- /dev/null +++ b/src/index.ts @@ -0,0 +1,5 @@ +import Dispatcher from 'core/Dispatcher' +import Complex from 'complex/type' +import Specifications from 'number/arithmetic' + +export default new Dispatcher(Specifications) diff --git a/src/number/arithmetic.ts b/src/number/arithmetic.ts new file mode 100644 index 0000000..90fe70e --- /dev/null +++ b/src/number/arithmetic.ts @@ -0,0 +1,29 @@ +import Specifications from './type' +import configDependency from '../core/Config' +/// + +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} diff --git a/src/number/type.ts b/src/number/type.ts new file mode 100644 index 0000000..4f775fe --- /dev/null +++ b/src/number/type.ts @@ -0,0 +1,17 @@ +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} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..aae3a94 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "target": "ES2022", + "rootDir": "./src", + "outDir": "./obj" + } +}