Skip to content

Commit 3d67ed4

Browse files
functions-module
1 parent 05b6d7e commit 3d67ed4

File tree

6 files changed

+495
-1
lines changed

6 files changed

+495
-1
lines changed

deno.json

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,56 @@
66
"noImplicitOverride": true,
77
"noUncheckedIndexedAccess": true
88
},
9+
<<<<<<< HEAD
10+
"imports": {
11+
"@deno/doc": "jsr:@deno/doc@0.137",
12+
"@deno/graph": "jsr:@deno/graph@^0.74",
13+
"@std/archive": "jsr:@std/archive@^0.225.0",
14+
"@std/assert": "jsr:@std/assert@^1.0.2",
15+
"@std/async": "jsr:@std/async@^1.0.3",
16+
"@std/bytes": "jsr:@std/bytes@^1.0.2-rc.3",
17+
"@std/cli": "jsr:@std/cli@^1.0.3",
18+
"@std/collections": "jsr:@std/collections@^1.0.5",
19+
"@std/crypto": "jsr:@std/crypto@^1.0.2-rc.1",
20+
"@std/csv": "jsr:@std/csv@^1.0.1",
21+
"@std/data-structures": "jsr:@std/data-structures@^1.0.1",
22+
"@std/datetime": "jsr:@std/datetime@^0.224.5",
23+
"@std/dotenv": "jsr:@std/dotenv@^0.225.0",
24+
"@std/encoding": "jsr:@std/encoding@^1.0.1",
25+
"@std/expect": "jsr:@std/expect@^1.0.0",
26+
"@std/fmt": "jsr:@std/fmt@^1.0.0",
27+
"@std/front-matter": "jsr:@std/front-matter@^1.0.1",
28+
"@std/fs": "jsr:@std/fs@^1.0.1",
29+
"@std/html": "jsr:@std/html@^1.0.1",
30+
"@std/http": "jsr:@std/http@^1.0.2",
31+
"@std/ini": "jsr:@std/ini@^1.0.0-rc.3",
32+
"@std/internal": "jsr:@std/internal@^1.0.1",
33+
"@std/io": "jsr:@std/io@^0.224.4",
34+
"@std/json": "jsr:@std/json@^1.0.0",
35+
"@std/jsonc": "jsr:@std/jsonc@^1.0.0",
36+
"@std/log": "jsr:@std/log@^0.224.5",
37+
"@std/media-types": "jsr:@std/media-types@^1.0.2",
38+
"@std/msgpack": "jsr:@std/msgpack@^1.0.0",
39+
"@std/net": "jsr:@std/net@^1.0.0",
40+
"@std/path": "jsr:@std/path@^1.0.2",
41+
"@std/regexp": "jsr:@std/regexp@^1.0.0",
42+
"@std/semver": "jsr:@std/semver@^1.0.1",
43+
"@std/streams": "jsr:@std/streams@^1.0.1",
44+
"@std/testing": "jsr:@std/testing@^1.0.0",
45+
"@std/text": "jsr:@std/text@^1.0.2",
46+
"@std/toml": "jsr:@std/toml@^1.0.0",
47+
"@std/ulid": "jsr:@std/ulid@^1.0.0",
48+
"@std/url": "jsr:@std/url@^0.225.0",
49+
"@std/uuid": "jsr:@std/uuid@^1.0.0",
50+
"@std/webgpu": "jsr:@std/webgpu@^0.224.5",
51+
"@std/yaml": "jsr:@std/yaml@^1.0.2",
52+
"automation/": "https://raw.githubusercontent.com/denoland/automation/0.10.0/",
53+
"graphviz": "npm:node-graphviz@^0.1.1",
54+
"npm:/typescript": "npm:typescript@5.4.4"
55+
},
56+
=======
957
"importMap": "./import_map.json",
58+
>>>>>>> 05b6d7eedd8e441cb8fa22078f377a6a37b4fa88
1059
"tasks": {
1160
"test": "deno test --unstable-http --unstable-webgpu --doc --allow-all --parallel --coverage --trace-leaks --clean",
1261
"test:browser": "git grep --name-only \"This module is browser compatible.\" | grep -v deno.json | grep -v .github/workflows | grep -v _tools | grep -v encoding/README.md | grep -v media_types/vendor/update.ts | xargs deno check --config browser-compat.tsconfig.json",
@@ -89,6 +138,7 @@
89138
"./ulid",
90139
"./uuid",
91140
"./webgpu",
92-
"./yaml"
141+
"./yaml",
142+
"./functions"
93143
]
94144
}

functions/curry.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// deno-lint-ignore no-explicit-any
2+
type AnyFunction = (...args: any[]) => any;
3+
4+
type Curried1Function<T, P1> = {
5+
(p1: P1): T;
6+
};
7+
type Curried2Function<T, P1, P2> = {
8+
(p1: P1): Curried1Function<T, P2>;
9+
(p1: P1, p2: P2): T;
10+
};
11+
type Curried3Function<T, P1, P2, P3> = {
12+
(p1: P1): Curried2Function<P2, P3, T>;
13+
(p1: P1, p2: P2): Curried1Function<T, P3>;
14+
(p1: P1, p2: P2, p3: P3): T;
15+
};
16+
type Curried4Function<T, P1, P2, P3, P4> = {
17+
(p1: P1): Curried3Function<P2, P3, P4, T>;
18+
(p1: P1, p2: P2): Curried2Function<T, P3, P2>;
19+
(p1: P1, p2: P2, p3: P3): Curried1Function<T, P4>;
20+
(p1: P1, p2: P2, p3: P3, p4: P4): T;
21+
};
22+
23+
/**
24+
* A function that returns a curried version of a given function
25+
*
26+
* @param {(p1: P1, p2: P2, … pn: Pn) => T} fn - The function to be curried.
27+
* @returns - A function which can be provided with only some of the arguments of the given function at each invocation, once all arguments have been provided the given function is called.
28+
* @example Usage
29+
* function add(a: number, b: number, c: number) {
30+
* return a + b + c + d;
31+
* }
32+
*
33+
* const curriedAdd = curry(add);
34+
* console.log(curriedAdd(1)(2)(3)); // 6
35+
* console.log(curriedAdd(1, 2)(3)); // 6
36+
* console.log(curriedAdd(1, 2, 3)); // 6
37+
*/
38+
39+
export function curry<T>(
40+
fn: () => T,
41+
): () => T;
42+
export function curry<T, P1>(
43+
fn: (p1: P1) => T,
44+
): (p1: P1) => T;
45+
export function curry<T, P1, P2>(
46+
fn: (p1: P1, p2: P2) => T,
47+
): Curried2Function<T, P1, P2>;
48+
export function curry<T, P1, P2, P3>(
49+
fn: (p1: P1, p2: P2, p3: P3) => T,
50+
): Curried3Function<T, P1, P2, P3>;
51+
export function curry<T, P1, P2, P3, P4>(
52+
fn: (p1: P1, p2: P2, p3: P3, p4: P4) => T,
53+
): Curried4Function<T, P1, P2, P3, P4>;
54+
export function curry(fn: AnyFunction) {
55+
return function curried(...args: any[]): any {
56+
if (args.length >= fn.length) {
57+
return fn(...args);
58+
} else {
59+
return (...moreArgs: any[]) => curried(...args, ...moreArgs);
60+
}
61+
};
62+
}
63+
64+
function add(a: number, b: number, c: number, d: number) {
65+
return a + b + c + d;
66+
}
67+
68+
const curriedAdd = curry(add);
69+
70+
if (import.meta.main) {
71+
const fnn = curriedAdd(1, 2);
72+
console.log(curriedAdd(1)(2)(3)(4));
73+
console.log(curriedAdd(1, 2)(3)(4));
74+
console.log(curriedAdd(1, 2, 3)(4));
75+
console.log(curriedAdd(1, 2, 3, 4));
76+
console.log(curriedAdd(1, 2, 3, 4));
77+
}

functions/deno.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "@std/functions",
3+
"version": "0.1.0",
4+
"exports": {
5+
".": "./mod.ts",
6+
"./curry": "./curry.ts",
7+
"./pipe": "./pipe.ts"
8+
}
9+
}

functions/mod.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
2+
// This module is browser compatible.
3+
4+
/**
5+
* Pure functions for common tasks around collection types like arrays and
6+
* objects.
7+
*
8+
* Inspired by
9+
* {@link https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/ | Kotlin's Collections}
10+
* package and {@link https://lodash.com/ | Lodash}.
11+
*
12+
* ```ts
13+
* import { intersect, sample, pick } from "@std/collections";
14+
* import { assertEquals, assertArrayIncludes } from "@std/assert";
15+
*
16+
* const lisaInterests = ["Cooking", "Music", "Hiking"];
17+
* const kimInterests = ["Music", "Tennis", "Cooking"];
18+
*
19+
* assertEquals(intersect(lisaInterests, kimInterests), ["Cooking", "Music"]);
20+
*
21+
* assertArrayIncludes(lisaInterests, [sample(lisaInterests)]);
22+
*
23+
* const cat = { name: "Lulu", age: 3, breed: "Ragdoll" };
24+
*
25+
* assertEquals(pick(cat, ["name", "breed"]), { name: "Lulu", breed: "Ragdoll"});
26+
* ```
27+
*
28+
* @module
29+
*/
30+
31+
export * from "./curry.ts";
32+
export * from "./pipe.ts";

functions/pipe.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// deno-lint-ignore-file no-explicit-any
2+
type AnyFunc = (...arg: any) => any;
3+
4+
type LastFnReturnType<F extends Array<AnyFunc>, Else = never> = F extends [
5+
...any[],
6+
(...arg: any) => infer R,
7+
] ? R
8+
: Else;
9+
10+
// inspired by https://dev.to/ecyrbe/how-to-use-advanced-typescript-to-define-a-pipe-function-381h
11+
type PipeArgs<F extends AnyFunc[], Acc extends AnyFunc[] = []> = F extends [
12+
(...args: infer A) => infer B,
13+
] ? [...Acc, (...args: A) => B]
14+
: F extends [(...args: infer A) => any, ...infer Tail]
15+
? Tail extends [(arg: infer B) => any, ...any[]]
16+
? PipeArgs<Tail, [...Acc, (...args: A) => B]>
17+
: Acc
18+
: Acc;
19+
20+
export function pipe<FirstFn extends AnyFunc, F extends AnyFunc[]>(
21+
firstFn: FirstFn,
22+
...fns: PipeArgs<F> extends F ? F : PipeArgs<F>
23+
): (arg: Parameters<FirstFn>[0]) => LastFnReturnType<F, ReturnType<FirstFn>> {
24+
return (arg: Parameters<FirstFn>[0]) =>
25+
(fns as AnyFunc[]).reduce((acc, fn) => fn(acc), firstFn(arg));
26+
}
27+
28+
if (import.meta.main) {
29+
const res = pipe(Math.abs, Math.sqrt, Math.floor);
30+
res(-2); // 1
31+
}

0 commit comments

Comments
 (0)