From 8a2ae79c905b3f16fcaa77567f40c2131359abc7 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Sun, 25 Sep 2022 10:39:16 -0400 Subject: [PATCH] feat: New array-style overloading Step two didn't work so well, so this is actually step three. Avoids too much redundant type information. --- README.md | 6 ++- src/steps/three.ts | 12 +++++ src/steps/two.ts | 102 +++++++++++++++++++++++++++++++++++++++++++ src/util/overload.ts | 17 ++++++++ tsconfig.json | 5 ++- 5 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 src/steps/three.ts create mode 100644 src/steps/two.ts create mode 100644 src/util/overload.ts diff --git a/README.md b/README.md index 26631ec..9277561 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,10 @@ Proof of concepts for a PocoMath-adjacent approach to a possible math.ts (TypeSc Roadmap: -1. Install over.ts and get an example of add with number and bigint implementations working with it. -2. Use the builder pattern to get add working with its implmentations defined incrementally before producing the final overload. +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.] +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. [IN PROGRESS] 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. 5. Incorporate a subtract method that works with one generic implementation that works for both number and bigint with dependencies on add and negate. diff --git a/src/steps/three.ts b/src/steps/three.ts new file mode 100644 index 0000000..e76253a --- /dev/null +++ b/src/steps/three.ts @@ -0,0 +1,12 @@ +import overload from '../util/overload.js' + +const adder = overload([ + (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) => 'Yay' + x + y +]) + +console.log(adder(1, 2)) +console.log(adder('a', 'b')) diff --git a/src/steps/two.ts b/src/steps/two.ts new file mode 100644 index 0000000..6e47772 --- /dev/null +++ b/src/steps/two.ts @@ -0,0 +1,102 @@ +import { useTypes } from 'over.ts/src/index.js'; + +const types = { + number: (x: unknown): x is number => typeof x === 'number', + bigint: (x: unknown): x is bigint => typeof x === 'bigint' +} + +const overload = useTypes(types) + +type TypeName = keyof typeof types + +//type Tuple = T | `${T}, ${Tuple}` +//type TypeTuple = Tuple + +type CheckTuple = T extends TypeName + ? T + : T extends `${TypeName}, ${infer Rest}` + ? CheckTuple extends string ? T : false + : false + +type CheckDecl = T extends `${infer Args} -> ${TypeName}` + ? CheckTuple extends string ? T : never + : never + +function check(d: CheckDecl) { + console.log(d) +} + +check('number, bigint -> bigint') +check('bigint -> number') + +// check('string -> number') // would be error as desired + +class ImpBuilder { + addImp(decl: CheckDecl, imp: U) { + return Object.assign(this, {[decl]: imp} as Record) + } +// finalize>(this: T) { +// return overload(this as Omit) + // } + finalize() { + delete this.addImp + delete this.finalize + return this + } +} + +function fixImps(imps: T): Omit { + const res = Object.assign({}, imps) + delete res.addImp + return res +} + +function overloadImps(imps: T) { + return overload(imps) +} + +const negateImps = new ImpBuilder() + .addImp('number -> number', (a: number) => -a) + .addImp('bigint -> bigint', (a: bigint) => -a) + +const negate = overload(negateImps.finalize() as Omit) + +console.log('Negation of 5 is', negate(5)) +console.log('Negation of 5n is', negate(5n)) + +const addImps = new ImpBuilder() // imagining this is in some "initialize a proto-bundle" file +addImps.addImp('number, number -> number', (a: number, b: number) => a+b) // imagining this line in the `number` 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) // We wrap everything up elsewhere + +console.log('Sum of 5 and 7 is', add(5,7)) +console.log('Sum of 5n and 7n is', add(5n, 7n)) + +try { + //@ts-expect-error + console.log('Mixed sum is', add(5n, 7)) +} catch { + console.log('Mixed sum errored as expected.') +} + +/***** +const addImps = new ImpBuilder() +//addImps.addImp('number, number -> number', (a: number, b: number) => a+b) +const subAddImps = addImps.addImp('number, number -> number', (a: number, b: number) => a+b).addImp('bigint, bigint -> bigint', (a: bigint, b: bigint) => { + console.log('adding bigints') + return a+b +}) +//const finalAddImps = addImps.finalize() + +const add = overload(subAddImps.finalize() as Omit) + +console.log('Sum of 5 and 7 is', add(5,7)) +console.log('Sum of 5n and 7n is', add(5n, 7n)) + +try { + //@ts-expect-error + console.log('Mixed sum is', add(5n, 7)) +} catch { + console.log('Mixed sum errored as expected.') +} +****/ diff --git a/src/util/overload.ts b/src/util/overload.ts new file mode 100644 index 0000000..71ab3fa --- /dev/null +++ b/src/util/overload.ts @@ -0,0 +1,17 @@ +type UnionToIntersection = + (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) + ? I + : never; + +export default function overload( + imps: T): UnionToIntersection { + return ((...a: any[]) => { + for (let i = 0; i < imps.length; ++i) { + try { + const val = imps[i](...a) + return val + } catch { + } + } + }) +} diff --git a/tsconfig.json b/tsconfig.json index 5741128..99d3a5d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,8 @@ "rootDir": "src", "outDir": "obj" }, - "files": [ - "src/steps/one.ts" + "include": [ + "src/steps/three.ts", + "src/util/*.ts" ] } -- 2.34.1