Compare commits

..

No commits in common. "main" and "steptwo" have entirely different histories.

10 changed files with 14 additions and 369 deletions

View File

@ -7,8 +7,8 @@ Roadmap:
1. Install over.ts and get an example of add with number and bigint implementations working with it. [DONE] 1. Install over.ts and get an example of add with number and bigint implementations working with it. [DONE]
2. Use the builder pattern to get add working with its implmentations defined incrementally before producing the final overload. [Didn't quite work the way we wnated, but maybe we can do an alternative later.] 2. Use the builder pattern to get add working with its implmentations defined incrementally before producing the final overload. [Didn't quite work the way we wnated, but maybe we can do an alternative later.]
3. Make a version of over.ts, call it util/overload.ts, that takes an array of implementations without redundant type annotation. [DONE] 3. Make a version of over.ts, call it util/overload.ts, that takes an array of implementations without redundant type annotation. [DONE]
4. Improve that version of overload with rtti to select the implementation without the implementations having to throw errors. [DONE] 4. Improve that version of overload with rtti to select the implementation without the implementations having to throw errors. [IN PROGRESS]
5. Use the builder pattern to get a single object with both an add and a negate method, with both defined incrementally, working. [DONE] 3. Use the builder pattern to get a single object with both an add and a negate method, with both defined incrementally, working.
4. Incorporate a subtract method that works on numbers and bigint by separate definitions but with dependencies on add and negate. 4. Incorporate a subtract method that works on numbers and bigint by separate definitions but with dependencies on add and negate.
5. Incorporate a subtract method that works with one generic implementation that works for both number and bigint with dependencies on add and negate. 5. Incorporate a subtract method that works with one generic implementation that works for both number and bigint with dependencies on add and negate.
5. Attempt to eliminate redundant specification of implementation signatures. 5. Attempt to eliminate redundant specification of implementation signatures.

View File

@ -5,7 +5,6 @@
main: 'index.js', main: 'index.js',
scripts: { scripts: {
test: 'echo "Error: no test specified" && exit 1', test: 'echo "Error: no test specified" && exit 1',
build: 'ttsc -b',
}, },
keywords: [ keywords: [
'math', 'math',
@ -19,13 +18,6 @@
license: 'Apache-2.0', license: 'Apache-2.0',
dependencies: { dependencies: {
'over.ts': 'github:m93a/over.ts', 'over.ts': 'github:m93a/over.ts',
'reflect-metadata': '^0.1.13',
typescript: '^4.8.2', typescript: '^4.8.2',
'typescript-rtti': '^0.8.2',
},
devDependencies: {
'@types/node': '^18.7.21',
'ts-node': '^10.9.1',
ttypescript: '^1.5.13',
}, },
} }

View File

@ -1,204 +1,20 @@
lockfileVersion: 5.4 lockfileVersion: 5.4
specifiers: specifiers:
'@types/node': ^18.7.21
over.ts: github:m93a/over.ts over.ts: github:m93a/over.ts
reflect-metadata: ^0.1.13
ts-node: ^10.9.1
ttypescript: ^1.5.13
typescript: ^4.8.2 typescript: ^4.8.2
typescript-rtti: ^0.8.2
dependencies: dependencies:
over.ts: github.com/m93a/over.ts/0fd6e18afd4ca5a23c9e09d1fcd6b7357b642247 over.ts: github.com/m93a/over.ts/0fd6e18afd4ca5a23c9e09d1fcd6b7357b642247
reflect-metadata: 0.1.13
typescript: 4.8.2 typescript: 4.8.2
typescript-rtti: 0.8.2_jbmzfhhdwyaatac7voo5jihpea
devDependencies:
'@types/node': 18.7.21
ts-node: 10.9.1_xxlomja3uhaizt5vuokco7nale
ttypescript: 1.5.13_s5ojjbx2isjkawqptqpitvy25q
packages: packages:
/@cspotcode/source-map-support/0.8.1:
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
engines: {node: '>=12'}
dependencies:
'@jridgewell/trace-mapping': 0.3.9
dev: true
/@jridgewell/resolve-uri/3.1.0:
resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
engines: {node: '>=6.0.0'}
dev: true
/@jridgewell/sourcemap-codec/1.4.14:
resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
dev: true
/@jridgewell/trace-mapping/0.3.9:
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
dependencies:
'@jridgewell/resolve-uri': 3.1.0
'@jridgewell/sourcemap-codec': 1.4.14
dev: true
/@tsconfig/node10/1.0.9:
resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==}
dev: true
/@tsconfig/node12/1.0.11:
resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==}
dev: true
/@tsconfig/node14/1.0.3:
resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==}
dev: true
/@tsconfig/node16/1.0.3:
resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==}
dev: true
/@types/node/18.7.21:
resolution: {integrity: sha512-rLFzK5bhM0YPyCoTC8bolBjMk7bwnZ8qeZUBslBfjZQou2ssJdWslx9CZ8DGM+Dx7QXQiiTVZ/6QO6kwtHkZCA==}
dev: true
/acorn-walk/8.2.0:
resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==}
engines: {node: '>=0.4.0'}
dev: true
/acorn/8.8.0:
resolution: {integrity: sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==}
engines: {node: '>=0.4.0'}
hasBin: true
dev: true
/arg/4.1.3:
resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
dev: true
/create-require/1.1.1:
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
dev: true
/diff/4.0.2:
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
engines: {node: '>=0.3.1'}
dev: true
/function-bind/1.1.1:
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
dev: true
/has/1.0.3:
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
engines: {node: '>= 0.4.0'}
dependencies:
function-bind: 1.1.1
dev: true
/is-core-module/2.10.0:
resolution: {integrity: sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==}
dependencies:
has: 1.0.3
dev: true
/make-error/1.3.6:
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
dev: true
/path-parse/1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
dev: true
/reflect-metadata/0.1.13:
resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==}
dev: false
/resolve/1.22.1:
resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
hasBin: true
dependencies:
is-core-module: 2.10.0
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
dev: true
/supports-preserve-symlinks-flag/1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
dev: true
/ts-node/10.9.1_xxlomja3uhaizt5vuokco7nale:
resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==}
hasBin: true
peerDependencies:
'@swc/core': '>=1.2.50'
'@swc/wasm': '>=1.2.50'
'@types/node': '*'
typescript: '>=2.7'
peerDependenciesMeta:
'@swc/core':
optional: true
'@swc/wasm':
optional: true
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.9
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.3
'@types/node': 18.7.21
acorn: 8.8.0
acorn-walk: 8.2.0
arg: 4.1.3
create-require: 1.1.1
diff: 4.0.2
make-error: 1.3.6
typescript: 4.8.2
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
dev: true
/ttypescript/1.5.13_s5ojjbx2isjkawqptqpitvy25q:
resolution: {integrity: sha512-KT/RBfGGlVJFqEI8cVvI3nMsmYcFvPSZh8bU0qX+pAwbi7/ABmYkzn7l/K8skw0xmYjVCoyaV6WLsBQxdadybQ==}
hasBin: true
peerDependencies:
ts-node: '>=8.0.2'
typescript: '>=3.2.2'
dependencies:
resolve: 1.22.1
ts-node: 10.9.1_xxlomja3uhaizt5vuokco7nale
typescript: 4.8.2
dev: true
/typescript-rtti/0.8.2_jbmzfhhdwyaatac7voo5jihpea:
resolution: {integrity: sha512-MZUHMX+Up1+7dYaAcOYSTxKc4PNLNXhuXYBkIzEKvQvxJ6xp7wcjTtiIYmB9+RJxRH5/9phZLqKTMpy20aQ4Aw==}
engines: {node: '>=10'}
peerDependencies:
reflect-metadata: ^0.1.13
typescript: '4.6'
dependencies:
reflect-metadata: 0.1.13
typescript: 4.8.2
dev: false
/typescript/4.8.2: /typescript/4.8.2:
resolution: {integrity: sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==} resolution: {integrity: sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==}
engines: {node: '>=4.2.0'} engines: {node: '>=4.2.0'}
hasBin: true hasBin: true
dev: false
/v8-compile-cache-lib/3.0.1:
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
dev: true
/yn/3.1.1:
resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
engines: {node: '>=6'}
dev: true
github.com/m93a/over.ts/0fd6e18afd4ca5a23c9e09d1fcd6b7357b642247: github.com/m93a/over.ts/0fd6e18afd4ca5a23c9e09d1fcd6b7357b642247:
resolution: {tarball: https://codeload.github.com/m93a/over.ts/tar.gz/0fd6e18afd4ca5a23c9e09d1fcd6b7357b642247} resolution: {tarball: https://codeload.github.com/m93a/over.ts/tar.gz/0fd6e18afd4ca5a23c9e09d1fcd6b7357b642247}

View File

@ -1,26 +0,0 @@
import 'reflect-metadata'
import {reflect} from 'typescript-rtti'
import {merge, overloadValues} from '../util/overload.js'
const numImps = {
add: [(x: number, y: number) => x + y],
negate: [(x: number) => -x]
} as const
const strImps = {
add: [(x: string, y: string) => x + ', ' + y],
negate: [(x: string) => 'NOT ' + x]
} as const
const merger = merge(numImps)
const mathImps = merger.with(strImps).imps()
const math = overloadValues(mathImps)
console.log(math.add(1.5, 2.5))
console.log(math.add('One and a half', 'Two and a half'))
console.log(math.negate(3.5))
console.log(math.negate('Three and a half'))
//@ts-expect-error
console.log(math.add(1.5, 'Two and a half'))

View File

@ -1,26 +0,0 @@
import 'reflect-metadata'
import overload from '../util/overload.js'
const addImps = [
(x: number, y: number) => {
if (typeof x === 'number' && typeof y === 'number') return x + y
throw new TypeError('Can only add numbers')
},
(x: string, y: string) => {
if (typeof x === 'string' && typeof y === 'string') {
return 'Yay' + x + y
}
throw new TypeError('or strings')
},
(x: bigint, final?: string) => x.toString() + final
] as const
const adder = overload(addImps)
console.log(adder(1, 2))
console.log(adder('a', 'b'))
console.log(adder(2n))
// And make sure typescript complains on signatures not covered by an imp:
//@ts-expect-error
console.log(adder(2n, 4n))

View File

@ -1,4 +1,4 @@
import { useTypes } from '../../node_modules/over.ts/src/index.js'; import { useTypes } from 'over.ts/src/index.js';
const types = { const types = {
number: (x: unknown): x is number => typeof x === 'number', number: (x: unknown): x is number => typeof x === 'number',

View File

@ -6,10 +6,7 @@ const adder = overload([
throw new TypeError('Can only add numbers') throw new TypeError('Can only add numbers')
}, },
(x: string, y: string) => 'Yay' + x + y (x: string, y: string) => 'Yay' + x + y
] as const) ])
console.log(adder(1, 2)) console.log(adder(1, 2))
console.log(adder('a', 'b')) console.log(adder('a', 'b'))
//@ts-expect-error
console.log(adder(3n, 4n))

View File

@ -1,4 +1,4 @@
import { useTypes } from 'over.ts'; import { useTypes } from 'over.ts/src/index.js';
const types = { const types = {
number: (x: unknown): x is number => typeof x === 'number', number: (x: unknown): x is number => typeof x === 'number',
@ -69,8 +69,6 @@ addImps.addImp('number, number -> number', (a: number, b: number) => a+b) // ima
const addAll = addImps.addImp('bigint, bigint -> bigint', (a: bigint, b: bigint) => a+b) // and this is in the `bigint` file const addAll = addImps.addImp('bigint, bigint -> bigint', (a: bigint, b: bigint) => a+b) // and this is in the `bigint` file
const add = overload(addAll.finalize() as Omit<typeof addAll, 'finalize'|'addImp'>) // We wrap everything up elsewhere const add = overload(addAll.finalize() as Omit<typeof addAll, 'finalize'|'addImp'>) // We wrap everything up elsewhere
// This should **NOT** be an error but sadly it is:
//@ts-expect-error
console.log('Sum of 5 and 7 is', add(5,7)) console.log('Sum of 5 and 7 is', add(5,7))
console.log('Sum of 5n and 7n is', add(5n, 7n)) console.log('Sum of 5n and 7n is', add(5n, 7n))

View File

@ -1,117 +1,17 @@
import {reflect, CallSite} from 'typescript-rtti'
type UnionToIntersection<U> = type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) (U extends any ? (k: U) => void : never) extends ((k: infer I) => void)
? I ? I
: never; : never;
export default function overload<T extends readonly [...any[]]>( export default function overload<T extends readonly [...any[]]>(
imps: T, callSite?: CallSite): UnionToIntersection<T[number]> { imps: T): UnionToIntersection<T[number]> {
const impTypes = reflect(callSite).parameters[0].elements
return <any>((...a: any[]) => { return <any>((...a: any[]) => {
for (let i = 0; i < imps.length; ++i) { for (let i = 0; i < imps.length; ++i) {
const paramTypes = imps[i].functionType.parameters try {
|| impTypes[i].type.parameters const val = imps[i](...a)
let match = true return val
const haveArgs = a.length } catch {
let onArg = 0
for (const param of paramTypes) {
if (param.isRest) {
// All the rest of the arguments must be of param's type
match = a.slice(onArg).every(
arg => param.type.matchesValue(arg))
break
} }
if (onArg === haveArgs) {
// We've used all of the arguments, so better be optional
match = param.isOptional
break
} }
// This argument must match this param's type and both are used
match = param.type.matchesValue(a[onArg])
onArg += 1
if (!match) break
}
// Make sure we used all the arguments
match &&= (onArg == haveArgs)
if (match) return imps[i](...a)
}
throw new TypeError(
`Actual arguments ${a} of type ${a.map(arg => typeof arg)} `
+ 'did not match any implementation')
}) })
} }
type AVO = Record<string, readonly [...any[]]>
type MergeArrayValues<L extends AVO, R extends AVO> =
Pick<L, Exclude<keyof L, keyof R>>
& Pick<R, Exclude<keyof R, keyof L>>
& { [P in (keyof L & keyof R)]: [...L[P], ...R[P]] }
function mergeImps<T extends AVO, U extends AVO>(
impT: T, impU: U): MergeArrayValues<T, U> {
const dummy = Object.assign({}, impT, impU)
const result = {} as AVO
for (const key in dummy) {
if (key in impT) {
if (key in impU) {
const k = key as Extract<keyof T & keyof U, string>
result[k] = [...impT[k], ...impU[k]]
} else {
result[key] = impT[key]
}
} else {
result[key] = impU[key]
}
}
return result as MergeArrayValues<T, U>
}
class ImpMerger<T extends AVO> {
implementations: T
constructor(imps: T) {
this.implementations = imps
}
with<U extends AVO>(
moreImps: U, callSite?: CallSite): ImpMerger<MergeArrayValues<T,U>> {
const Utype = reflect(callSite).parameters[0]
// Annotate the implementations with their RTTI while we have it
for (const [key, imps] of Object.entries(moreImps)) {
if (!(imps[0].functionType)) {
const tuple = Utype.members.find(m => m.name === key)
for (let i = 0; i < imps.length; ++i) {
imps[i].functionType = tuple.type.elements[i].type
}
}
}
return new ImpMerger(mergeImps<T,U>(this.implementations, moreImps))
}
imps(): T {
return this.implementations
}
}
function merge<T extends AVO>(impsObject: T, callSite?: CallSite) {
const Ttype = reflect(callSite).parameters[0]
// Annotate the implementations with their RTTI while we have it
for (const [key, imps] of Object.entries(impsObject)) {
if (!(imps[0].functionType)) {
const tuple = Ttype.members.find(m => m.name === key)
for (let i = 0; i < imps.length; ++i) {
imps[i].functionType = tuple.type.elements[i].type
}
}
}
return new ImpMerger(impsObject)
}
type OverloadedValues<T extends AVO> = {
[P in keyof T]: UnionToIntersection<T[P][number]>}
function overloadValues<T extends AVO>(impObject: T) : OverloadedValues<T> {
return Object.fromEntries(Object.entries(impObject).map(
([k, v]) => [k, overload(v)])) as OverloadedValues<T>
}
export {merge, mergeImps, overloadValues}

View File

@ -1,18 +1,12 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2022", "target": "ES2022",
"baseUrl": ".", "moduleResolution": "node",
"paths": { "rootDir": "src",
"over.ts": ["node_modules/over.ts/src/index.js"],
"typescript-rtti": ["node_modules/typescript-rtti/dist/index.js"]
},
"plugins": [{ "transform": "typescript-rtti/dist/transformer" }],
"rootDir": ".",
"outDir": "obj" "outDir": "obj"
}, },
"include": [ "include": [
"node_modules/over.ts/src/index.ts", "src/steps/three.ts",
"src/steps/*.ts",
"src/util/*.ts" "src/util/*.ts"
] ]
} }