diff --git a/src/Complex/all.ts b/src/Complex/all.ts index 3ff6311..320bb2a 100644 --- a/src/Complex/all.ts +++ b/src/Complex/all.ts @@ -1,6 +1,9 @@ import * as Complex from './native.js' import * as complex from './arithmetic.js' +import {$$typeMetadata} from 'ts-macros' export { complex } export {Complex} + +const tryit = $$typeMetadata!(true, true) diff --git a/src/Complex/arithmetic.ts b/src/Complex/arithmetic.ts index 0f11f2e..11a2d7e 100644 --- a/src/Complex/arithmetic.ts +++ b/src/Complex/arithmetic.ts @@ -2,7 +2,7 @@ import {Complex} from './type.js' import type { Dependencies, Signature, Returns, RealType, AliasOf } from '../interfaces/type.js' -import {$reflect} from '../interfaces/type.js' +import {$reflectGen, GENERIC} from '../interfaces/type.js' declare module "../interfaces/type" { interface Signatures { @@ -98,7 +98,11 @@ export const sqrt = return dep.divideReal(num, denom) } -$reflect!([ +export interface GenericImplementations { + kilroy: "was here" +} + +$reflectGen!(GENERIC, [ add, addReal, unaryMinus, conj, subtract, multiply, absquare, divideReal, reciprocal, divide, sqrt ]) diff --git a/src/Complex/predicate.ts b/src/Complex/predicate.ts index fe26906..625b6d2 100644 --- a/src/Complex/predicate.ts +++ b/src/Complex/predicate.ts @@ -1,6 +1,6 @@ import {Complex} from './type.js' import type {Dependencies, Signature} from '../interfaces/type.js' -import {$reflect} from '../interfaces/type.js' +import {$reflectGen, GENERIC, CONCRETE} from '../interfaces/type.js' export const isReal = (dep: Dependencies<'add' | 'equal' | 'isReal', T>): @@ -10,4 +10,5 @@ export const isReal = export const isSquare = (): Signature<'isSquare', Complex> => z => true // FIXME: not correct for Complex once we get there -$reflect!([isReal, isSquare]) +$reflectGen!(GENERIC, [isReal]) +$reflectGen!(CONCRETE, [isSquare]) diff --git a/src/core/Dispatcher.ts b/src/core/Dispatcher.ts index 4ede7cd..3dab510 100644 --- a/src/core/Dispatcher.ts +++ b/src/core/Dispatcher.ts @@ -6,6 +6,7 @@ */ import {parseReflectedType, ImplementationDef} from './parseReflectedType.js' +import type {ValueIntersectionByKeyUnion} from '../interfaces/type.js' // First helper types and functions for the Dispatcher @@ -35,50 +36,103 @@ type TypeSpecification = { infer?: (d: DependenciesType) => (z: unknown) => TypeName } -type SpecObject = Record +type Callable = (...args: any) => any +type SpecObject = Record type SpecificationsGroup = Record -export class Dispatcher { - installSpecification( - name: string, - defn: ImplementationDef, - 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, 'with signatures') - for (const signature of defn.fn.signatures) { - console.log(' ', signature.args, '=>', signature.returns) - } - if (defn.fn.aliasOf) { - console.log(' As an alias of', defn.fn.aliasOf) - } - //TODO: implement me - } - installType(name: TypeName, typespec: TypeSpecification) { - console.log('Pretending to install type', name, typespec) - //TODO: implement me - } - constructor(collection: SpecificationsGroup) { - const implementations = [] - for (const key in collection) { - console.log('Working on', key) - for (const identifier in collection[key]) { - const item = collection[key][identifier] - if (typeof item === 'function') { - implementations.push([key, identifier, item]) - } else { - console.log('Handling type', key, ':', identifier) - this.installType( - item.name, collection[key][identifier] as TypeSpecification) - } +// Find all of the keys of an object that are functions but not aliases +// to another operation +type BaseOperations = + {[K in keyof Obj]: + Obj[K] extends Callable + ? ReturnType['aliasOf'] extends (string | undefined) + ? never + : K extends string ? K : never + : never + }[keyof Obj] +// Gather all of the operations specified in a SpecificationsGroup +type DispatcherOperations = + {[K in keyof G]: BaseOperations}[keyof G] +// Get the type of a given operation in a SpecObject: +type BaseOperationSignature< + Obj extends SpecObject, + Name extends BaseOperations +> = ValueIntersectionByKeyUnion< + {[K in keyof Obj]: + Obj[K] extends Callable + ? K extends Name + ? ReturnType + : ReturnType['aliasOf'] extends (Name | undefined) + ? ReturnType : unknown + : unknown + }, + keyof Obj> +// Get the type of a given operation in a SpecificationsGroup +type OperationSignature< + G extends SpecificationsGroup, + Name extends DispatcherOperations +> = ValueIntersectionByKeyUnion< + {[K in keyof G]: + Name extends BaseOperations + ? BaseOperationSignature + : unknown}, + keyof G> +// Put it all together into the typing of the dispatcher +type DispatcherInterface = + {[Op in DispatcherOperations]: OperationSignature} + +export function createDispatcher( + collection: G): DispatcherInterface +{ + const implementations = [] + const types = {} // who knows what the type of this will be + const behaviors = {} // ditto + for (const key in collection) { + console.log('Working on', key) + for (const identifier in collection[key]) { + const item = collection[key][identifier] + if (typeof item === 'function') { + implementations.push([key, identifier, item]) + } else { + console.log('Handling type', key, ':', identifier) + installType( // In this design, installType would modify + // types by side effect; maybe Jos prefers some other + // factoring of this? + types, item.name, + collection[key][identifier] as TypeSpecification) } } - for (const trio of implementations) { - const [k, id, imp] = trio - console.log('Handling implementation', id, 'from', k) - this.installSpecification( - id, parseReflectedType(id, imp.reflectedType), imp) - } } + for (const trio of implementations) { + const [k, id, imp] = trio + console.log('Handling implementation', id, 'from', k) + installSpecification( + behaviors, id, parseReflectedType(id, imp.reflectedType), imp) + } + return {} as DispatcherInterface +} + +function installType( + types: Object, name: TypeName, typespec: TypeSpecification) +{ + console.log('Pretending to install type', name, typespec) + //TODO: implement me +} + +function installSpecification( + behaviors: Object, // Same issue as mentioned above with side effects... + name: string, + defn: ImplementationDef, + specification: 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, 'with signatures') + for (const signature of defn.fn.signatures) { + console.log(' ', signature.args, '=>', signature.returns) + } + if (defn.fn.aliasOf) { + console.log(' As an alias of', defn.fn.aliasOf) + } + //TODO: implement me } diff --git a/src/index.ts b/src/index.ts index e3377e5..cf46df1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,12 @@ import {inspect} from 'node:util' -import {Dispatcher} from './core/Dispatcher.js' +import {createDispatcher} from './core/Dispatcher.js' import * as Specifications from './all.js' -export default new Dispatcher(Specifications) +export Specifications +const math = createDispatcher(Specifications) +export default math + +console.log('Made', math.add} import {Complex} from './Complex/type.js' import {absquare as absquare_complex} from './Complex/arithmetic.js' diff --git a/src/interfaces/type.ts b/src/interfaces/type.ts index 3ab1b9a..da776d8 100644 --- a/src/interfaces/type.ts +++ b/src/interfaces/type.ts @@ -15,7 +15,7 @@ import {$$typeToString} from 'ts-macros' * but that's OK, the generic parameter doesn't hurt in those cases. ****/ -type ValueIntersectionByKeyUnion = { +export type ValueIntersectionByKeyUnion = { [P in TKey]: (k: T[P])=>void } [TKey] extends ((k: infer I)=>void) ? I : never @@ -77,7 +77,7 @@ type SignatureKey = keyof Signatures export type Signature, T> = Signatures[Name] export type Returns, T> = ReturnType[Name]> -type Deps = T extends unknown ? { [K in keyof T]: T[K] } : never; +export type Deps = T extends unknown ? { [K in keyof T]: T[K] } : never; export type Dependencies, T> = Deps<{[K in Name]: Signature}> @@ -88,3 +88,34 @@ export function $reflect(tup: ImplTuple) { +[[tup], (elt: T) => elt.reflectedType = $$typeToString!(true, false, true)] } +export function $genImps(tup: ImplTuple) { + +[[tup], (elt: U) => { + export interface GenericImplementations { + [$$text!(elt)]: typeof elt + } + }] +} + +export function $genImpsT(tup: ImplTuple) { + +[[tup], (elt: U) => { + export interface GenericImplementations { + [$$text!(elt)]: typeof elt + } + }] +} +export const GENERIC = true +export const CONCRETE = false +export function $reflectGen(generic: boolean, tup: ImplTuple) { + +[[tup], (elt: T) => { + elt.reflectedType = $$typeToString!(true, false, true) + if (generic) { + export interface GenericImplementations { + [$$text!(elt)]: typeof elt + } + } else { + export interface GenericImplementations { + [$$text!(elt)]: typeof elt + } + } + }] +} diff --git a/src/numbers/native.ts b/src/numbers/native.ts index ea2f66b..50c97f0 100644 --- a/src/numbers/native.ts +++ b/src/numbers/native.ts @@ -1,4 +1,9 @@ +import {GenericImplementations} from './predicate.js' +import {$$typeToString} from 'ts-macros' export * from './type.js' export * from './arithmetic.js' export * from './predicate.js' export * from './relational.js' + +const test: ReturnType['isReal']> = + (a: number) => true // = "oops" would fail as desired. diff --git a/src/numbers/predicate.ts b/src/numbers/predicate.ts index 0a51d65..e0a4907 100644 --- a/src/numbers/predicate.ts +++ b/src/numbers/predicate.ts @@ -1,7 +1,11 @@ import type {Signature} from '../interfaces/type.js' -import {$reflect} from '../interfaces/type.js' - +import {$reflectGen, CONCRETE} from '../interfaces/type.js' +import {$$typeToString} from 'ts-macros' export const isReal = (): Signature<'isReal', number> => (a) => true export const isSquare = (): Signature<'isSquare', number> => (a) => a >= 0 -$reflect!([isReal, isSquare]) +export interface GenericImplementations { + kilroy: "was here" +} + +$reflectGen!(CONCRETE, [isReal, isSquare]) diff --git a/tsconfig.json b/tsconfig.json index 63c0b04..edf7502 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,7 @@ "rootDir": "./src", "outDir": "./build", "moduleResolution": "nodenext", + "declaration": true, "plugins": [ { "transform": "ts-macros/dist/type-resolve", "transformProgram": true