refactor: Explore final patterns for runtime typing with ts-macros

The idea is to have a single macro which lets you reflect multiple
  implementations. That's provided in this commit. However, there
  is a hitch with implementations that have no dependencies: the
  reflectedType property did not exist on the Signature type. However,
  if you add it, then the Signature type becomes opaque, so we would
  have to look up signatures; but that appears to be tricky, as there
  doesn't seem to be a way to reflect the full generic type of the
  Signatures interface.

  So this commit provides three example resolutions:
  (A) Export a type RTT and all no-dependency implementations have to
      intersect with RTT (see 'add' in number/arithmetic.ts)
  (B) Write no-dependency implementations as functions of no arguments
      (representing no dependencies passed in) returning the actual
      implementation (see 'subtract' in number/arithmetic.ts)
  (C) Make a special DSignature<> generic type (short for "Direct Signature"
      used only (and always) for implementations with no dependencies, and
      a matching $Dreflect!() macro for generating their type info.

  Of course, there may be other possibilities I haven't thought of. But we
  need to pick one. I don't think it matters _too_ much, since there only
  a small fraction of all implementations have no dependencies.
This commit is contained in:
Glen Whitney 2023-10-15 19:58:38 -07:00
parent 49b1332917
commit ebe7cf831e
5 changed files with 42 additions and 37 deletions

View File

@ -2,7 +2,7 @@ import {Complex} from './type.js'
import type {
Dependencies, Signature, Returns, RealType, AliasOf
} from '../interfaces/type.js'
import {$reflecType} from '../interfaces/type.js'
import {$reflect} from '../interfaces/type.js'
declare module "../interfaces/type" {
interface Signatures<T> {
@ -82,8 +82,7 @@ export const sqrt =
addTR: Signature<'addReal', T>,
addRR: Signature<'add', RealType<T>>,
addCR: Signature<'addReal', Complex<T>>
}):
Signature<'sqrt', Complex<T>> =>
}): Signature<'sqrt', Complex<T>> =>
z => {
const myabs = dep.conservativeSqrt(dep.absquare(z))
const r = dep.re(z)
@ -98,4 +97,4 @@ export const sqrt =
const denom = dep.conservativeSqrt(denomsq)
return dep.divideReal(num, denom)
}
$reflecType!(sqrt)
$reflect!([sqrt])

View File

@ -1,7 +1,9 @@
import type {Dependencies, Signature} from '../interfaces/type.js'
import {$reflect} from '../interfaces/type.js'
export const square = $reflect!('square',
<T>(dep: Dependencies<'multiply', T>) => (z:T) => dep.multiply(z, z))
export const square =
<T>(dep: Dependencies<'multiply', T>): Signature<'square', T> =>
z => dep.multiply(z, z)
// z => dep.fooBar(z, z) // fails as desired
// z => dep.multiply(z, 'foo') // still fails as desired
$reflect!([square])

View File

@ -1,12 +1,20 @@
import {inspect} from 'node:util'
import {Dispatcher} from './core/Dispatcher.js'
import {Signatures, Deps} from './interfaces/type.js'
import {$$typeToString} from 'ts-macros'
import * as Specifications from './all.js'
class Placeholder {}
const allSignatures =
$$typeToString!<Deps<Signatures<Placeholder>>>(true, false, true)
console.log('Found signatures', allSignatures)
export default new Dispatcher(Specifications)
import {Complex} from './Complex/type.js'
import {absquare as absquare_complex} from './Complex/arithmetic.js'
import { parseReflectedType, split } from './core/parseReflectedType.js'
import {parseReflectedType} from './core/parseReflectedType.js'
const mockRealAdd = (a: number, b: number) => a+b
const mockComplexAbsquare = (z: Complex<number>) => z.re*z.re + z.im*z.im
@ -36,7 +44,11 @@ console.log('Result of sqrt(-4)=', sqrt(-4))
console.log()
console.log('1) NUMBER SQRT')
console.log(`1.1) REFLECTED TYPE: "${Specifications.numbers.sqrt.reflectedType}"`)
console.log('1.2) PARSED TYPE:', inspect(parseReflectedType('sqrt', Specifications.numbers.sqrt.reflectedType), { depth: null, colors: true }))
console.log(
'1.2) PARSED TYPE:',
inspect(
parseReflectedType('sqrt', Specifications.numbers.sqrt.reflectedType),
{ depth: null, colors: true }))
console.log()
console.log('2) GENERIC SQUARE')
@ -47,9 +59,3 @@ console.log()
console.log('3) COMPLEX SQRT')
console.log(`1.1) REFLECTED TYPE: "${Specifications.complex.sqrt.reflectedType}"`)
console.log('3.2) PARSED TYPE:', inspect(parseReflectedType('sqrt', Specifications.complex.sqrt.reflectedType), { depth: null, colors: true }))
// FIXME: cleanup
// console.log()
// console.log('split', split('hello**world**how**are**you', '**'))
// console.log('split', split('hello(test**world)**how**are**you', '**'))
// console.log('split', split('<T>(dep: { multiply: (a: T, b: T) => T; }) => (z: T) => T', '=>'))

View File

@ -1,4 +1,4 @@
import {$$typeToString, $$ident, $$define} from 'ts-macros'
import {$$typeToString} from 'ts-macros'
/*****
* Every typocomath type has some associated types; they need
@ -74,25 +74,23 @@ export interface Signatures<T> {
type SignatureKey<T> = keyof Signatures<T>
export type Signature<Name extends SignatureKey<T>, T> = Signatures<T>[Name]
export type Signature<Name extends SignatureKey<T>, T> =
Signatures<T>[Name]
export type DSignature<Name extends SignatureKey<T>, T> =
Signatures<T>[Name] & {reflectedType?: string, actualType?: Signatures<T>[Name]}
export type Returns<Name extends SignatureKey<T>, T> = ReturnType<Signatures<T>[Name]>
type Deps<T> = T extends unknown ? { [K in keyof T]: T[K] } : never;
export type Deps<T> = T extends unknown ? { [K in keyof T]: T[K] } : never;
export type Dependencies<Name extends SignatureKey<T>, T> = Deps<{[K in Name]: Signature<K, T>}>
export type AliasOf<Name extends string, T> = T & {aliasOf?: Name}
// For defining implementations with type reflection
export function $reflect<Impl>(name: string, expr: Impl) : Impl & { reflectedType: string} {
$$define!(name, expr, false, false);
$$ident!(name).reflectedType = $$typeToString!<Impl>(true, false, true);
return $$ident!(name)
}
export function $reflecType<Impl>(expr: Impl) {
expr.reflectedType = $$typeToString!<Impl>(true, false, true);
}
export function $reflecTypes<ImplTuple>(tup: ImplTuple) {
export function $reflect<ImplTuple>(tup: ImplTuple) {
+[[tup], <T>(elt: T) =>
elt.reflectedType = $$typeToString!<T>(true, false, true)]
}
export function $Dreflect<ImplTuple>(tup: ImplTuple) {
+[[tup], <T>(elt: T) =>
elt.reflectedType = $$typeToString!<T['actualType']>(true, false, true)]
}
export type RTT = {reflectedType?: string}

View File

@ -1,11 +1,11 @@
import type {configDependency} from '../core/Config.js'
import type {Dependencies, Signature} from '../interfaces/type.js'
import { $reflecType, $reflecTypes } from '../interfaces/type.js'
import type {Dependencies, Signature, RTT, DSignature} from '../interfaces/type.js'
import {$reflect, $Dreflect} from '../interfaces/type.js'
export const add /*: Signature<'add', number>*/ = (a, b) => a + b
export const add: Signature<'add', number> & RTT = (a, b) => a + b
export const unaryMinus: Signature<'unaryMinus', number> = a => -a
export const conj /*: Signature<'conj', number>*/ = a => a
export const subtract /*: Signature<'subtract', number>*/ = (a, b) => a - b
export const conj: DSignature<'conj', number> = a => a
export const subtract = (): Signature<'subtract', number> => (a, b) => a - b
export const multiply: Signature<'multiply', number> = (a, b) => a * b
export const absquare: Signature<'absquare', number> = a => a * a
export const reciprocal: Signature<'reciprocal', number> = a => 1 / a
@ -26,5 +26,5 @@ export const sqrt =
return dep.complex(0, Math.sqrt(unaryMinus(a)))
}
}
$reflecType!(sqrt)
$reflecTypes!([add, conj, subtract])
$reflect!([add, sqrt, subtract])
$Dreflect!([conj])