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
|
||||
272
HOTScript.ts
272
HOTScript.ts
@@ -1,156 +1,170 @@
|
||||
/**
|
||||
* Generic helpers
|
||||
*/
|
||||
|
||||
interface Fn {
|
||||
input: unknown;
|
||||
export namespace HOT {
|
||||
export interface Fn {
|
||||
args: unknown[];
|
||||
output: unknown;
|
||||
}
|
||||
|
||||
export type Apply<fn extends HOT.Fn, args> = (fn & {
|
||||
args: args;
|
||||
})["output"];
|
||||
|
||||
export type Call<fn extends HOT.Fn, arg1> = (fn & {
|
||||
args: [arg1];
|
||||
})["output"];
|
||||
|
||||
export type Call2<fn extends HOT.Fn, arg1, arg2> = (fn & {
|
||||
args: [arg1, arg2];
|
||||
})["output"];
|
||||
|
||||
export type Call3<fn extends HOT.Fn, arg1, arg2, arg3> = (fn & {
|
||||
args: [arg1, arg2, arg3];
|
||||
})["output"];
|
||||
|
||||
export type Call4<fn extends HOT.Fn, arg1, arg2, arg3, arg4> = (fn & {
|
||||
args: [arg1, arg2, arg3, arg3];
|
||||
})["output"];
|
||||
|
||||
export type Reduce<xs, acc, fn extends HOT.Fn> = xs extends [
|
||||
infer first,
|
||||
...infer rest
|
||||
]
|
||||
? 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
|
||||
>;
|
||||
}
|
||||
|
||||
type Call<fn extends Fn, input> = (fn & { input: input })["output"];
|
||||
/**
|
||||
* Strings
|
||||
*/
|
||||
|
||||
type Stringifiable = string | number | boolean | bigint | null | undefined;
|
||||
export namespace Strings {
|
||||
export type Stringifiable =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| bigint
|
||||
| null
|
||||
| undefined;
|
||||
|
||||
type SplitImpl<
|
||||
type SplitImpl<
|
||||
str,
|
||||
sep extends string,
|
||||
output extends any[] = []
|
||||
> = str extends `${infer first}${sep}${infer rest}`
|
||||
> = 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>]
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
interface FilterFn<fn extends Fn> extends Fn {
|
||||
output: this["input"] extends {
|
||||
acc: infer acc extends any[];
|
||||
item: infer item;
|
||||
/**
|
||||
* 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>;
|
||||
}
|
||||
? Call<fn, item> extends true
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
interface TupleMap<fn extends Fn> extends Fn {
|
||||
output: Reduce<this["input"], [], MapFn<fn>>;
|
||||
}
|
||||
export interface Map<fn extends HOT.Fn> extends HOT.Fn {
|
||||
output: HOT.Reduce<this["args"][0], [], MapFn<fn>>;
|
||||
}
|
||||
|
||||
interface TupleReduce<init, fn extends Fn> extends Fn {
|
||||
output: Reduce<this["input"], init, fn>;
|
||||
}
|
||||
export interface Reduce<init, fn extends HOT.Fn> extends HOT.Fn {
|
||||
output: HOT.Reduce<this["args"][0], init, fn>;
|
||||
}
|
||||
|
||||
interface TupleFilter<fn extends Fn> extends Fn {
|
||||
output: Reduce<this["input"], [], FilterFn<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"]]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lets use that!
|
||||
* Objects
|
||||
*/
|
||||
|
||||
interface ToPhrase extends Fn {
|
||||
output: `number is ${Extract<this["input"], string | number | boolean>}`;
|
||||
}
|
||||
export namespace Objects {}
|
||||
|
||||
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
|
||||
]
|
||||
>;
|
||||
/**
|
||||
* 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": {
|
||||
"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