code: structure code in modules
This commit is contained in:
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
lib
|
||||||
|
dist
|
||||||
|
notes.md
|
||||||
|
.vscode/
|
||||||
|
tracing_output_folder/
|
||||||
|
*.tgz
|
||||||
310
HOTScript.ts
310
HOTScript.ts
@@ -1,156 +1,170 @@
|
|||||||
/**
|
export namespace HOT {
|
||||||
* Generic helpers
|
export interface Fn {
|
||||||
*/
|
args: unknown[];
|
||||||
|
output: unknown;
|
||||||
interface Fn {
|
|
||||||
input: unknown;
|
|
||||||
output: unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
type Call<fn extends Fn, input> = (fn & { input: input })["output"];
|
|
||||||
|
|
||||||
type Stringifiable = string | number | boolean | bigint | null | undefined;
|
|
||||||
|
|
||||||
type SplitImpl<
|
|
||||||
str,
|
|
||||||
sep extends string,
|
|
||||||
output extends any[] = []
|
|
||||||
> = str extends `${infer first}${sep}${infer rest}`
|
|
||||||
? SplitImpl<rest, sep, [...output, first]>
|
|
||||||
: output;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generic Array operations
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reduce
|
|
||||||
*/
|
|
||||||
type Reduce<xs, acc, fn extends Fn> = xs extends [infer first, ...infer rest]
|
|
||||||
? Reduce<rest, Call<fn, { acc: acc; item: first }>, fn>
|
|
||||||
: acc;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define Map in terms of Reduce
|
|
||||||
*/
|
|
||||||
interface MapFn<fn extends Fn> extends Fn {
|
|
||||||
output: this["input"] extends {
|
|
||||||
acc: infer acc extends any[];
|
|
||||||
item: infer item;
|
|
||||||
}
|
}
|
||||||
? [...acc, Call<fn, item>]
|
|
||||||
: never;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FilterFn<fn extends Fn> extends Fn {
|
export type Apply<fn extends HOT.Fn, args> = (fn & {
|
||||||
output: this["input"] extends {
|
args: args;
|
||||||
acc: infer acc extends any[];
|
})["output"];
|
||||||
item: infer item;
|
|
||||||
}
|
|
||||||
? Call<fn, item> extends true
|
|
||||||
? [...acc, item]
|
|
||||||
: acc
|
|
||||||
: never;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TupleMap<fn extends Fn> extends Fn {
|
export type Call<fn extends HOT.Fn, arg1> = (fn & {
|
||||||
output: Reduce<this["input"], [], MapFn<fn>>;
|
args: [arg1];
|
||||||
}
|
})["output"];
|
||||||
|
|
||||||
interface TupleReduce<init, fn extends Fn> extends Fn {
|
export type Call2<fn extends HOT.Fn, arg1, arg2> = (fn & {
|
||||||
output: Reduce<this["input"], init, fn>;
|
args: [arg1, arg2];
|
||||||
}
|
})["output"];
|
||||||
|
|
||||||
interface TupleFilter<fn extends Fn> extends Fn {
|
export type Call3<fn extends HOT.Fn, arg1, arg2, arg3> = (fn & {
|
||||||
output: Reduce<this["input"], [], FilterFn<fn>>;
|
args: [arg1, arg2, arg3];
|
||||||
}
|
})["output"];
|
||||||
|
|
||||||
/**
|
export type Call4<fn extends HOT.Fn, arg1, arg2, arg3, arg4> = (fn & {
|
||||||
* Lets use that!
|
args: [arg1, arg2, arg3, arg3];
|
||||||
*/
|
})["output"];
|
||||||
|
|
||||||
interface ToPhrase extends Fn {
|
export type Reduce<xs, acc, fn extends HOT.Fn> = xs extends [
|
||||||
output: `number is ${Extract<this["input"], string | number | boolean>}`;
|
infer first,
|
||||||
}
|
...infer rest
|
||||||
|
|
||||||
type ys = Call<TupleMap<ToPhrase>, [1, 2, 3]>;
|
|
||||||
// ^?
|
|
||||||
|
|
||||||
type MakeRange<n, acc extends any[] = []> = acc["length"] extends n
|
|
||||||
? acc
|
|
||||||
: MakeRange<n, [...acc, acc["length"]]>;
|
|
||||||
|
|
||||||
type AddNumbers<a, b> = [...MakeRange<a>, ...MakeRange<b>]["length"];
|
|
||||||
|
|
||||||
type StringToNumber<str> = str extends `${infer n extends number}` ? n : never;
|
|
||||||
|
|
||||||
interface Add2 extends Fn {
|
|
||||||
output: this["input"] extends {
|
|
||||||
acc: infer acc;
|
|
||||||
item: infer item;
|
|
||||||
}
|
|
||||||
? AddNumbers<acc, item>
|
|
||||||
: never;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface JoinReducer<sep extends string> extends Fn {
|
|
||||||
output: this["input"] extends {
|
|
||||||
acc: infer acc extends Stringifiable;
|
|
||||||
item: infer item extends Stringifiable;
|
|
||||||
}
|
|
||||||
? `${acc extends "" ? "" : `${acc}${sep}`}${item}`
|
|
||||||
: never;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PipeFn extends Fn {
|
|
||||||
output: this["input"] extends {
|
|
||||||
acc: infer acc;
|
|
||||||
item: infer fn extends Fn;
|
|
||||||
}
|
|
||||||
? Call<fn, acc>
|
|
||||||
: never;
|
|
||||||
}
|
|
||||||
|
|
||||||
type Pipe<init, xs extends Fn[]> = Reduce<xs, init, PipeFn>;
|
|
||||||
|
|
||||||
interface Add<n> extends Fn {
|
|
||||||
output: AddNumbers<this["input"], n>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MapAdd<n> extends Fn {
|
|
||||||
output: Call<TupleMap<Add<n>>, this["input"]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Join<sep extends string> extends Fn {
|
|
||||||
output: Reduce<this["input"], "", JoinReducer<sep>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Split<sep extends string> extends Fn {
|
|
||||||
output: SplitImpl<this["input"], sep>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ToNumber extends Fn {
|
|
||||||
output: this["input"] extends `${infer n extends number}` ? n : never;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MapToNumber extends Fn {
|
|
||||||
output: Call<TupleMap<ToNumber>, this["input"]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Sum extends Fn {
|
|
||||||
output: Reduce<this["input"], 0, Add2>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
type result = Pipe<
|
|
||||||
// ^?
|
|
||||||
[1, 2, 3, 4, 3, 4],
|
|
||||||
[
|
|
||||||
TupleMap<Add<3>>,
|
|
||||||
Join<'.'>,
|
|
||||||
Split<'.'>,
|
|
||||||
TupleMap<ToNumber>,
|
|
||||||
TupleMap<Add<10>>,
|
|
||||||
Sum
|
|
||||||
]
|
]
|
||||||
>;
|
? Reduce<rest, HOT.Call2<fn, acc, first>, fn>
|
||||||
|
: acc;
|
||||||
|
|
||||||
|
export type ReduceRight<xs, acc, fn extends HOT.Fn> = xs extends [
|
||||||
|
...infer rest,
|
||||||
|
infer last
|
||||||
|
]
|
||||||
|
? ReduceRight<rest, HOT.Call2<fn, acc, last>, fn>
|
||||||
|
: acc;
|
||||||
|
|
||||||
|
interface PipeFn extends HOT.Fn {
|
||||||
|
output: this["args"] extends [infer acc, infer fn extends HOT.Fn]
|
||||||
|
? HOT.Call<fn, acc>
|
||||||
|
: never;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Pipe<init, xs extends HOT.Fn[]> = HOT.Reduce<xs, init, PipeFn>;
|
||||||
|
|
||||||
|
interface PipeRightFn extends HOT.Fn {
|
||||||
|
output: this["args"] extends [infer acc, infer fn extends HOT.Fn]
|
||||||
|
? HOT.Call<fn, acc>
|
||||||
|
: never;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PipeRight<xs extends HOT.Fn[], init> = HOT.ReduceRight<
|
||||||
|
xs,
|
||||||
|
init,
|
||||||
|
PipeRightFn
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strings
|
||||||
|
*/
|
||||||
|
|
||||||
|
export namespace Strings {
|
||||||
|
export type Stringifiable =
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| boolean
|
||||||
|
| bigint
|
||||||
|
| null
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
type SplitImpl<
|
||||||
|
str,
|
||||||
|
sep extends string,
|
||||||
|
output extends any[] = []
|
||||||
|
> = str extends `${infer first}${sep}${infer rest}`
|
||||||
|
? SplitImpl<rest, sep, [...output, first]>
|
||||||
|
: output;
|
||||||
|
|
||||||
|
interface JoinReducer<sep extends string> extends HOT.Fn {
|
||||||
|
output: this["args"] extends [
|
||||||
|
infer acc extends Strings.Stringifiable,
|
||||||
|
infer item extends Strings.Stringifiable
|
||||||
|
]
|
||||||
|
? `${acc extends "" ? "" : `${acc}${sep}`}${item}`
|
||||||
|
: never;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Join<sep extends string> extends HOT.Fn {
|
||||||
|
output: HOT.Reduce<this["args"][0], "", JoinReducer<sep>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Split<sep extends string> extends HOT.Fn {
|
||||||
|
output: SplitImpl<this["args"][0], sep>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ToNumber extends HOT.Fn {
|
||||||
|
output: this["args"][0] extends `${infer n extends number}` ? n : never;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Numbers
|
||||||
|
*/
|
||||||
|
export namespace Numbers {
|
||||||
|
type Add2Impl<a, b> = [...Tuples.Range<a>, ...Tuples.Range<b>]["length"];
|
||||||
|
|
||||||
|
export interface Add<n> extends HOT.Fn {
|
||||||
|
output: Add2Impl<this["args"][0], n>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Add2 extends HOT.Fn {
|
||||||
|
output: this["args"] extends [infer acc, infer item]
|
||||||
|
? Add2Impl<acc, item>
|
||||||
|
: never;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tuples
|
||||||
|
*/
|
||||||
|
export namespace Tuples {
|
||||||
|
interface MapFn<fn extends HOT.Fn> extends HOT.Fn {
|
||||||
|
output: this["args"] extends [infer acc extends any[], infer item]
|
||||||
|
? [...acc, HOT.Call<fn, item>]
|
||||||
|
: never;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FilterFn<fn extends HOT.Fn> extends HOT.Fn {
|
||||||
|
output: this["args"] extends [infer acc extends any[], infer item]
|
||||||
|
? HOT.Call<fn, item> extends true
|
||||||
|
? [...acc, item]
|
||||||
|
: acc
|
||||||
|
: never;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Map<fn extends HOT.Fn> extends HOT.Fn {
|
||||||
|
output: HOT.Reduce<this["args"][0], [], MapFn<fn>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Reduce<init, fn extends HOT.Fn> extends HOT.Fn {
|
||||||
|
output: HOT.Reduce<this["args"][0], init, fn>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Filter<fn extends HOT.Fn> extends HOT.Fn {
|
||||||
|
output: HOT.Reduce<this["args"][0], [], FilterFn<fn>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Sum extends HOT.Fn {
|
||||||
|
output: HOT.Reduce<this["args"][0], 0, Numbers.Add2>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Range<n, acc extends any[] = []> = acc["length"] extends n
|
||||||
|
? acc
|
||||||
|
: Range<n, [...acc, acc["length"]]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Objects
|
||||||
|
*/
|
||||||
|
|
||||||
|
export namespace Objects {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unions
|
||||||
|
*/
|
||||||
|
export namespace Unions {}
|
||||||
|
|||||||
45
README.md
45
README.md
@@ -17,3 +17,48 @@ type result = Pipe<
|
|||||||
]
|
]
|
||||||
>;
|
>;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
- [ ] Composition
|
||||||
|
- [ ] Pipe
|
||||||
|
- [ ] Compose
|
||||||
|
- [ ] Tuples
|
||||||
|
- [ ] Head
|
||||||
|
- [ ] Tail
|
||||||
|
- [ ] Map
|
||||||
|
- [ ] FlatMap
|
||||||
|
- [ ] Filter
|
||||||
|
- [ ] Reduce
|
||||||
|
- [ ] Find
|
||||||
|
- [ ] Zip
|
||||||
|
- [ ] Partition
|
||||||
|
- [ ] Drop n
|
||||||
|
- [ ] Take n
|
||||||
|
- [ ] TakeWhile
|
||||||
|
- [ ] Join separator
|
||||||
|
- [ ] Object
|
||||||
|
- [ ] Assign
|
||||||
|
- [ ] FromEntries
|
||||||
|
- [ ] Entries
|
||||||
|
- [ ] Pick
|
||||||
|
- [ ] PickBy
|
||||||
|
- [ ] Omit
|
||||||
|
- [ ] OmitBy
|
||||||
|
- [ ] Union
|
||||||
|
- [ ] Assign
|
||||||
|
- [ ] FromEntries
|
||||||
|
- [ ] Entries
|
||||||
|
- [ ] Extract
|
||||||
|
- [ ] ExtractBy
|
||||||
|
- [ ] Exclude
|
||||||
|
- [ ] ExcludeBy
|
||||||
|
- [ ] String
|
||||||
|
- [ ] Concat
|
||||||
|
- [ ] Uppercase
|
||||||
|
- [ ] Lowercase
|
||||||
|
- [ ] Capitalize
|
||||||
|
- [ ] Uncapitalize
|
||||||
|
- [ ] Split separator
|
||||||
|
- [ ] Words
|
||||||
|
- [ ] Reduce
|
||||||
|
|||||||
3492
package-lock.json
generated
Normal file
3492
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -15,5 +15,10 @@
|
|||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/gvergnaud/PipeScript/issues"
|
"url": "https://github.com/gvergnaud/PipeScript/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/gvergnaud/PipeScript#readme"
|
"homepage": "https://github.com/gvergnaud/PipeScript#readme",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/jest": "^29.4.0",
|
||||||
|
"jest": "^29.4.2",
|
||||||
|
"typescript": "^4.9.5"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
57
test/HOTScript.test.ts
Normal file
57
test/HOTScript.test.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { HOT, Numbers, Strings, Tuples } from "../HOTScript";
|
||||||
|
import { Equal, Expect } from "./helpers";
|
||||||
|
|
||||||
|
describe("HOTScript", () => {
|
||||||
|
describe("Composition", () => {
|
||||||
|
describe("Pipe", () => {
|
||||||
|
type res1 = HOT.Pipe<
|
||||||
|
// ^?
|
||||||
|
[1, 2, 3, 4, 3, 4],
|
||||||
|
[
|
||||||
|
Tuples.Map<Numbers.Add<3>>,
|
||||||
|
Strings.Join<".">,
|
||||||
|
Strings.Split<".">,
|
||||||
|
Tuples.Map<Strings.ToNumber>,
|
||||||
|
Tuples.Map<Numbers.Add<10>>,
|
||||||
|
Tuples.Sum
|
||||||
|
]
|
||||||
|
>;
|
||||||
|
|
||||||
|
type tes1 = Expect<Equal<res1, 78>>;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("PipeRight", () => {
|
||||||
|
type res1 = HOT.PipeRight<
|
||||||
|
// ^?
|
||||||
|
[
|
||||||
|
Tuples.Sum,
|
||||||
|
Tuples.Map<Numbers.Add<10>>,
|
||||||
|
Tuples.Map<Strings.ToNumber>,
|
||||||
|
Strings.Split<".">,
|
||||||
|
Strings.Join<".">,
|
||||||
|
Tuples.Map<Numbers.Add<3>>
|
||||||
|
],
|
||||||
|
[1, 2, 3, 4, 3, 4]
|
||||||
|
>;
|
||||||
|
|
||||||
|
type tes1 = Expect<Equal<res1, 78>>;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Tuples", () => {
|
||||||
|
describe("Map", () => {
|
||||||
|
interface ToPhrase extends HOT.Fn {
|
||||||
|
output: `number is ${Extract<
|
||||||
|
this["args"][0],
|
||||||
|
string | number | boolean
|
||||||
|
>}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
type res1 = HOT.Call<Tuples.Map<ToPhrase>, [1, 2, 3]>;
|
||||||
|
// ^?
|
||||||
|
type tes1 = Expect<
|
||||||
|
Equal<res1, ["number is 1", "number is 2", "number is 3"]>
|
||||||
|
>;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
19
test/helpers.ts
Normal file
19
test/helpers.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
export type Equal<a, b> = (<T>() => T extends a ? 1 : 2) extends <
|
||||||
|
T
|
||||||
|
>() => T extends b ? 1 : 2
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
|
|
||||||
|
export type Expect<a extends true> = a;
|
||||||
|
|
||||||
|
export type Some<bools extends boolean[]> = true extends bools[number]
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
|
|
||||||
|
export type Every<bools extends boolean[]> = bools[number] extends true
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
|
|
||||||
|
export type Extends<a, b> = [a] extends [b] ? true : false;
|
||||||
|
|
||||||
|
export type Not<a extends boolean> = a extends true ? false : true;
|
||||||
Reference in New Issue
Block a user