functions-module

This commit is contained in:
Guy Kedem 2024-10-24 14:08:18 +07:00
parent 05b6d7eedd
commit 3d67ed4c7e
6 changed files with 495 additions and 1 deletions

View File

@ -6,7 +6,56 @@
"noImplicitOverride": true,
"noUncheckedIndexedAccess": true
},
<<<<<<< HEAD
"imports": {
"@deno/doc": "jsr:@deno/doc@0.137",
"@deno/graph": "jsr:@deno/graph@^0.74",
"@std/archive": "jsr:@std/archive@^0.225.0",
"@std/assert": "jsr:@std/assert@^1.0.2",
"@std/async": "jsr:@std/async@^1.0.3",
"@std/bytes": "jsr:@std/bytes@^1.0.2-rc.3",
"@std/cli": "jsr:@std/cli@^1.0.3",
"@std/collections": "jsr:@std/collections@^1.0.5",
"@std/crypto": "jsr:@std/crypto@^1.0.2-rc.1",
"@std/csv": "jsr:@std/csv@^1.0.1",
"@std/data-structures": "jsr:@std/data-structures@^1.0.1",
"@std/datetime": "jsr:@std/datetime@^0.224.5",
"@std/dotenv": "jsr:@std/dotenv@^0.225.0",
"@std/encoding": "jsr:@std/encoding@^1.0.1",
"@std/expect": "jsr:@std/expect@^1.0.0",
"@std/fmt": "jsr:@std/fmt@^1.0.0",
"@std/front-matter": "jsr:@std/front-matter@^1.0.1",
"@std/fs": "jsr:@std/fs@^1.0.1",
"@std/html": "jsr:@std/html@^1.0.1",
"@std/http": "jsr:@std/http@^1.0.2",
"@std/ini": "jsr:@std/ini@^1.0.0-rc.3",
"@std/internal": "jsr:@std/internal@^1.0.1",
"@std/io": "jsr:@std/io@^0.224.4",
"@std/json": "jsr:@std/json@^1.0.0",
"@std/jsonc": "jsr:@std/jsonc@^1.0.0",
"@std/log": "jsr:@std/log@^0.224.5",
"@std/media-types": "jsr:@std/media-types@^1.0.2",
"@std/msgpack": "jsr:@std/msgpack@^1.0.0",
"@std/net": "jsr:@std/net@^1.0.0",
"@std/path": "jsr:@std/path@^1.0.2",
"@std/regexp": "jsr:@std/regexp@^1.0.0",
"@std/semver": "jsr:@std/semver@^1.0.1",
"@std/streams": "jsr:@std/streams@^1.0.1",
"@std/testing": "jsr:@std/testing@^1.0.0",
"@std/text": "jsr:@std/text@^1.0.2",
"@std/toml": "jsr:@std/toml@^1.0.0",
"@std/ulid": "jsr:@std/ulid@^1.0.0",
"@std/url": "jsr:@std/url@^0.225.0",
"@std/uuid": "jsr:@std/uuid@^1.0.0",
"@std/webgpu": "jsr:@std/webgpu@^0.224.5",
"@std/yaml": "jsr:@std/yaml@^1.0.2",
"automation/": "https://raw.githubusercontent.com/denoland/automation/0.10.0/",
"graphviz": "npm:node-graphviz@^0.1.1",
"npm:/typescript": "npm:typescript@5.4.4"
},
=======
"importMap": "./import_map.json",
>>>>>>> 05b6d7eedd8e441cb8fa22078f377a6a37b4fa88
"tasks": {
"test": "deno test --unstable-http --unstable-webgpu --doc --allow-all --parallel --coverage --trace-leaks --clean",
"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 @@
"./ulid",
"./uuid",
"./webgpu",
"./yaml"
"./yaml",
"./functions"
]
}

77
functions/curry.ts Normal file
View File

@ -0,0 +1,77 @@
// deno-lint-ignore no-explicit-any
type AnyFunction = (...args: any[]) => any;
type Curried1Function<T, P1> = {
(p1: P1): T;
};
type Curried2Function<T, P1, P2> = {
(p1: P1): Curried1Function<T, P2>;
(p1: P1, p2: P2): T;
};
type Curried3Function<T, P1, P2, P3> = {
(p1: P1): Curried2Function<P2, P3, T>;
(p1: P1, p2: P2): Curried1Function<T, P3>;
(p1: P1, p2: P2, p3: P3): T;
};
type Curried4Function<T, P1, P2, P3, P4> = {
(p1: P1): Curried3Function<P2, P3, P4, T>;
(p1: P1, p2: P2): Curried2Function<T, P3, P2>;
(p1: P1, p2: P2, p3: P3): Curried1Function<T, P4>;
(p1: P1, p2: P2, p3: P3, p4: P4): T;
};
/**
* A function that returns a curried version of a given function
*
* @param {(p1: P1, p2: P2, pn: Pn) => T} fn - The function to be curried.
* @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.
* @example Usage
* function add(a: number, b: number, c: number) {
* return a + b + c + d;
* }
*
* const curriedAdd = curry(add);
* console.log(curriedAdd(1)(2)(3)); // 6
* console.log(curriedAdd(1, 2)(3)); // 6
* console.log(curriedAdd(1, 2, 3)); // 6
*/
export function curry<T>(
fn: () => T,
): () => T;
export function curry<T, P1>(
fn: (p1: P1) => T,
): (p1: P1) => T;
export function curry<T, P1, P2>(
fn: (p1: P1, p2: P2) => T,
): Curried2Function<T, P1, P2>;
export function curry<T, P1, P2, P3>(
fn: (p1: P1, p2: P2, p3: P3) => T,
): Curried3Function<T, P1, P2, P3>;
export function curry<T, P1, P2, P3, P4>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4) => T,
): Curried4Function<T, P1, P2, P3, P4>;
export function curry(fn: AnyFunction) {
return function curried(...args: any[]): any {
if (args.length >= fn.length) {
return fn(...args);
} else {
return (...moreArgs: any[]) => curried(...args, ...moreArgs);
}
};
}
function add(a: number, b: number, c: number, d: number) {
return a + b + c + d;
}
const curriedAdd = curry(add);
if (import.meta.main) {
const fnn = curriedAdd(1, 2);
console.log(curriedAdd(1)(2)(3)(4));
console.log(curriedAdd(1, 2)(3)(4));
console.log(curriedAdd(1, 2, 3)(4));
console.log(curriedAdd(1, 2, 3, 4));
console.log(curriedAdd(1, 2, 3, 4));
}

9
functions/deno.json Normal file
View File

@ -0,0 +1,9 @@
{
"name": "@std/functions",
"version": "0.1.0",
"exports": {
".": "./mod.ts",
"./curry": "./curry.ts",
"./pipe": "./pipe.ts"
}
}

32
functions/mod.ts Normal file
View File

@ -0,0 +1,32 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
/**
* Pure functions for common tasks around collection types like arrays and
* objects.
*
* Inspired by
* {@link https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/ | Kotlin's Collections}
* package and {@link https://lodash.com/ | Lodash}.
*
* ```ts
* import { intersect, sample, pick } from "@std/collections";
* import { assertEquals, assertArrayIncludes } from "@std/assert";
*
* const lisaInterests = ["Cooking", "Music", "Hiking"];
* const kimInterests = ["Music", "Tennis", "Cooking"];
*
* assertEquals(intersect(lisaInterests, kimInterests), ["Cooking", "Music"]);
*
* assertArrayIncludes(lisaInterests, [sample(lisaInterests)]);
*
* const cat = { name: "Lulu", age: 3, breed: "Ragdoll" };
*
* assertEquals(pick(cat, ["name", "breed"]), { name: "Lulu", breed: "Ragdoll"});
* ```
*
* @module
*/
export * from "./curry.ts";
export * from "./pipe.ts";

31
functions/pipe.ts Normal file
View File

@ -0,0 +1,31 @@
// deno-lint-ignore-file no-explicit-any
type AnyFunc = (...arg: any) => any;
type LastFnReturnType<F extends Array<AnyFunc>, Else = never> = F extends [
...any[],
(...arg: any) => infer R,
] ? R
: Else;
// inspired by https://dev.to/ecyrbe/how-to-use-advanced-typescript-to-define-a-pipe-function-381h
type PipeArgs<F extends AnyFunc[], Acc extends AnyFunc[] = []> = F extends [
(...args: infer A) => infer B,
] ? [...Acc, (...args: A) => B]
: F extends [(...args: infer A) => any, ...infer Tail]
? Tail extends [(arg: infer B) => any, ...any[]]
? PipeArgs<Tail, [...Acc, (...args: A) => B]>
: Acc
: Acc;
export function pipe<FirstFn extends AnyFunc, F extends AnyFunc[]>(
firstFn: FirstFn,
...fns: PipeArgs<F> extends F ? F : PipeArgs<F>
): (arg: Parameters<FirstFn>[0]) => LastFnReturnType<F, ReturnType<FirstFn>> {
return (arg: Parameters<FirstFn>[0]) =>
(fns as AnyFunc[]).reduce((acc, fn) => fn(acc), firstFn(arg));
}
if (import.meta.main) {
const res = pipe(Math.abs, Math.sqrt, Math.floor);
res(-2); // 1
}

295
functions/set_arguments.ts Normal file
View File

@ -0,0 +1,295 @@
export function set_arguments<T, P1>(fn: (p1: P1) => T, p1: P1): () => T;
export function set_arguments<T, P1, P2>(
fn: (p1: P1, p2: P2) => T,
p1: P1,
p2: undefined,
): (p2: P2) => T;
export function set_arguments<T, P1, P2>(
fn: (p1: P1, p2: P2) => T,
p1: undefined,
p2: P2,
): (p1: P1) => T;
export function set_arguments<T, P1, P2>(
fn: (p1: P1, p2: P2) => T,
p1: P1,
p2: P2,
): () => T;
export function set_arguments<T, P1, P2, P3>(
fn: (p1: P1, p2: P2, p3: P3) => T,
p1: P1,
p2: P2,
p3: undefined,
): (p3: P3) => T;
export function set_arguments<T, P1, P2, P3>(
fn: (p1: P1, p2: P2, p3: P3) => T,
p1: P1,
p2: undefined,
p3: P3,
): (p2: P2) => T;
export function set_arguments<T, P1, P2, P3>(
fn: (p1: P1, p2: P2, p3: P3) => T,
p1: undefined,
p2: P2,
p3: P3,
): (p1: P1) => T;
export function set_arguments<T, P1, P2, P3>(
fn: (p1: P1, p2: P2, p3: P3) => T,
p1: P1,
p2: undefined,
p3: undefined,
): (p2: P2, p3: P3) => T;
export function set_arguments<T, P1, P2, P3>(
fn: (p1: P1, p2: P2, p3: P3) => T,
p1: undefined,
p2: P2,
p3: undefined,
): (p1: P1, p3: P3) => T;
export function set_arguments<T, P1, P2, P3>(
fn: (p1: P1, p2: P2, p3: P3) => T,
p1: undefined,
p2: undefined,
p3: P3,
): (p1: P1, p2: P2) => T;
export function set_arguments<T, P1, P2, P3>(
fn: (p1: P1, p2: P2, p3: P3) => T,
p1: P1,
p2: P2,
p3: P3,
): () => T;
export function set_arguments<T, P1, P2, P3, P4>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4) => T,
p1: P1,
p2: P2,
p3: P3,
p4: undefined,
): (p4: P4) => T;
export function set_arguments<T, P1, P2, P3, P4>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4) => T,
p1: P1,
p2: P2,
p3: undefined,
p4: P4,
): (p3: P3) => T;
export function set_arguments<T, P1, P2, P3, P4>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4) => T,
p1: P1,
p2: undefined,
p3: P3,
p4: P4,
): (p2: P2) => T;
export function set_arguments<T, P1, P2, P3, P4>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4) => T,
p1: undefined,
p2: P2,
p3: P3,
p4: P4,
): (p1: P1) => T;
export function set_arguments<T, P1, P2, P3, P4>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4) => T,
p1: P1,
p2: P2,
p3: undefined,
p4: undefined,
): (p3: P3, p4: P4) => T;
export function set_arguments<T, P1, P2, P3, P4>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4) => T,
p1: P1,
p2: undefined,
p3: P3,
p4: undefined,
): (p2: P2, p4: P4) => T;
export function set_arguments<T, P1, P2, P3, P4>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4) => T,
p1: undefined,
p2: P2,
p3: undefined,
p4: P4,
): (p1: P1, p3: P3) => T;
export function set_arguments<T, P1, P2, P3, P4>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4) => T,
p1: undefined,
p2: undefined,
p3: P3,
p4: P4,
): (p1: P1, p2: P2) => T;
export function set_arguments<T, P1, P2, P3, P4>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4) => T,
p1: undefined,
p2: P2,
p3: P3,
p4: undefined,
): (p1: P1, p4: P4) => T;
export function set_arguments<T, P1, P2, P3, P4>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4) => T,
p1: P1,
p2: undefined,
p3: undefined,
p4: P4,
): (p2: P2, p3: P3) => T;
export function set_arguments<T, P1, P2, P3, P4>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4) => T,
p1: undefined,
p2: undefined,
p3: undefined,
p4: P4,
): (p1: P1, p2: P2, p3: P3) => T;
export function set_arguments<T, P1, P2, P3, P4>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4) => T,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
): () => T;
export function set_arguments<T, P1, P2, P3, P4, P5>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => T,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: undefined,
): (p5: P5) => T;
export function set_arguments<T, P1, P2, P3, P4, P5>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => T,
p1: P1,
p2: P2,
p3: P3,
p4: undefined,
p5: P5,
): (p4: P4) => T;
export function set_arguments<T, P1, P2, P3, P4, P5>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => T,
p1: P1,
p2: P2,
p3: undefined,
p4: P4,
p5: P5,
): (p3: P3) => T;
export function set_arguments<T, P1, P2, P3, P4, P5>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => T,
p1: P1,
p2: undefined,
p3: P3,
p4: P4,
p5: P5,
): (p2: P2) => T;
export function set_arguments<T, P1, P2, P3, P4, P5>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => T,
p1: undefined,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
): (p1: P1) => T;
export function set_arguments<T, P1, P2, P3, P4, P5>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => T,
p1: P1,
p2: P2,
p3: P3,
p4: undefined,
p5: undefined,
): (p4: P4, p5: P5) => T;
export function set_arguments<T, P1, P2, P3, P4, P5>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => T,
p1: P1,
p2: P2,
p3: undefined,
p4: P4,
p5: undefined,
): (p3: P3, p5: P5) => T;
export function set_arguments<T, P1, P2, P3, P4, P5>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => T,
p1: P1,
p2: undefined,
p3: P3,
p4: undefined,
p5: P5,
): (p2: P2, p4: P4) => T;
export function set_arguments<T, P1, P2, P3, P4, P5>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => T,
p1: undefined,
p2: P2,
p3: P3,
p4: undefined,
p5: P5,
): (p1: P1, p4: P4) => T;
export function set_arguments<T, P1, P2, P3, P4, P5>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => T,
p1: P1,
p2: undefined,
p3: undefined,
p4: P4,
p5: P5,
): (p2: P2, p3: P3) => T;
export function set_arguments<T, P1, P2, P3, P4, P5>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => T,
p1: undefined,
p2: P2,
p3: undefined,
p4: P4,
p5: P5,
): (p1: P1, p3: P3) => T;
export function set_arguments<T, P1, P2, P3, P4, P5>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => T,
p1: undefined,
p2: undefined,
p3: P3,
p4: P4,
p5: P5,
): (p1: P1, p2: P2) => T;
export function set_arguments<T, P1, P2, P3, P4, P5>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => T,
p1: undefined,
p2: P2,
p3: P3,
p4: P4,
p5: undefined,
): (p1: P1, p5: P5) => T;
export function set_arguments<T, P1, P2, P3, P4, P5>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => T,
p1: P1,
p2: undefined,
p3: undefined,
p4: P4,
p5: undefined,
): (p2: P2, p3: P3, p5: P5) => T;
export function set_arguments<T, P1, P2, P3, P4, P5>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => T,
p1: undefined,
p2: undefined,
p3: P3,
p4: P4,
p5: undefined,
): (p1: P1, p2: P2, p5: P5) => T;
export function set_arguments<T, P1, P2, P3, P4, P5>(
fn: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => T,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
): () => T;
export function set_arguments(
fn: (...args: any[]) => any,
...setArgs: any[]
) {
// ensure setArgs at least as long as the function's arguments
while (setArgs.length < fn.length) {
setArgs.push(undefined);
}
return (...providedArgs: any[]) => {
// insert each argument at the index of undefined
const mergedArgs = setArgs.map((arg) =>
arg === undefined ? providedArgs.shift() : arg
);
return fn(...mergedArgs);
};
}
if (import.meta.main) {
const divide = (a: number, b: number) => a / b;
const invert = set_arguments(divide, 1, undefined);
const r = invert(2);
console.log(r); // 1/2
}