Skip to content

Commit

Permalink
feat(partial): add partial function
Browse files Browse the repository at this point in the history
  • Loading branch information
TomokiMiyauci committed Jun 5, 2023
1 parent 5f90247 commit 3379335
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 1 deletion.
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,30 @@ curriedFn("", 0)(false);
curriedFn("", 0, false);
```

## Partial application

Creates a new "bound function".

It has the following characteristics:

- The `length` property is 0.
- The `name` property is `partial`.

### partial

Partially applies function arguments.

```ts
import { partial } from "https://deno.land/x/curry@$VERSION/mod.ts";

declare const fn: (a: string, b: number, c: boolean) => void;

const ternary = partial(fn);
const binary = partial(fn, "");
const unary = partial(fn, "", 0);
const nullary = partial(fn, "", 0, false);
```

## API

See [deno doc](https://deno.land/x/curry/mod.ts) for all APIs.
Expand Down
11 changes: 11 additions & 0 deletions _dev_deps.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
// Copyright 2023-latest Tomoki Miyauchi. All rights reserved. MIT license.

export { assertEquals } from "https://deno.land/std@0.190.0/testing/asserts.ts";
export {
afterEach,
beforeEach,
describe,
it,
} from "https://deno.land/std@0.190.0/testing/bdd.ts";
export {
assertSpyCallArgs,
type Spy,
spy,
} from "https://deno.land/std@0.190.0/testing/mock.ts";
export const assertEqualsTypes = <T, U extends T = T>(_actual?: U): void => {};
5 changes: 4 additions & 1 deletion deno.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"https://deno.land/std@0.190.0/fmt/colors.ts": "d67e3cd9f472535241a8e410d33423980bec45047e343577554d3356e1f0ef4e",
"https://deno.land/std@0.190.0/testing/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea",
"https://deno.land/std@0.190.0/testing/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7",
"https://deno.land/std@0.190.0/testing/asserts.ts": "e16d98b4d73ffc4ed498d717307a12500ae4f2cbe668f1a215632d19fcffc22f"
"https://deno.land/std@0.190.0/testing/_test_suite.ts": "30f018feeb3835f12ab198d8a518f9089b1bcb2e8c838a8b615ab10d5005465c",
"https://deno.land/std@0.190.0/testing/asserts.ts": "e16d98b4d73ffc4ed498d717307a12500ae4f2cbe668f1a215632d19fcffc22f",
"https://deno.land/std@0.190.0/testing/bdd.ts": "59f7f7503066d66a12e50ace81bfffae5b735b6be1208f5684b630ae6b4de1d0",
"https://deno.land/std@0.190.0/testing/mock.ts": "220ed9b8151cb2cac141043d4cfea7c47673fab5d18d1c1f0943297c8afb5d13"
}
}
1 change: 1 addition & 0 deletions mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
// This module is browser compatible.

export { curry } from "./curry.ts";
export { partial } from "./partial.ts";
31 changes: 31 additions & 0 deletions partial.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2023-latest Tomoki Miyauchi. All rights reserved. MIT license.
// This module is browser compatible.

/** Partially applies function arguments.
*
* @example
* ```ts
* import { partial } from "https://deno.land/x/curry@$VERSION/partial.ts";
*
* declare const fn: (a: string, b: number, c: boolean) => void;
*
* const ternary = partial(fn);
* const binary = partial(fn, "");
* const unary = partial(fn, "", 0);
* const nullary = partial(fn, "", 0, false);
* ```
*/
export function partial<
T extends readonly unknown[],
U extends readonly unknown[],
R,
>(
fn: (...args: readonly [...T, ...U]) => R,
...args: T
): (...rest: U) => R {
function partial(...rest: U): R {
return fn(...args, ...rest);
}

return partial;
}
132 changes: 132 additions & 0 deletions partial_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// Copyright 2023-latest Tomoki Miyauchi. All rights reserved. MIT license.

import { partial } from "./partial.ts";
import {
assertEquals,
assertSpyCallArgs,
beforeEach,
describe,
it,
Spy,
spy,
} from "./_dev_deps.ts";

interface Arity1 {
(arg: string): unknown;
}

interface Arity2 {
(arg: string, arg2: number): unknown;
}

interface Arity3 {
(arg: string, arg2: number, arg3: boolean): unknown;
}

type Fn2Spy<T> = T extends (this: infer T, ...args: infer Args) => infer R
? Spy<T, Args, R>
: unknown;

describe("partial", () => {
describe("arity 1", function () {
interface Context {
fn: Fn2Spy<Arity1>;
}
beforeEach<Context>(function () {
this.fn = spy((arg1: string) => arg1);
});
it<Context>("should pass 0 argument", function () {
const arg = "";
const fn = partial(this.fn);

assertEquals(fn(arg), arg);
assertSpyCallArgs(this.fn, 0, [arg]);
});

it<Context>("should pass 1 argument", function () {
const arg = "";
const fn = partial(this.fn, arg);

assertEquals(fn(), arg);
assertSpyCallArgs(this.fn, 0, [arg]);
});
});

describe("arity 2", function () {
interface Context {
fn: Fn2Spy<Arity2>;
}

const arg = "";
const arg2 = 0;

beforeEach<Context>(function () {
this.fn = spy((arg1: string, arg2: number): string => arg1 + arg2);
});

it<Context>("should pass 0 argument", function () {
const fn = partial(this.fn);

assertEquals(fn(arg, arg2), arg + arg2);
assertSpyCallArgs(this.fn, 0, [arg, arg2]);
});

it<Context>("should pass 1 argument", function () {
const fn = partial(this.fn, arg);

assertEquals(fn(arg2), arg + arg2);
assertSpyCallArgs(this.fn, 0, [arg, arg2]);
});

it<Context>("should pass 2 argument", function () {
const fn = partial(this.fn, arg, arg2);

assertEquals(fn(), arg + arg2);
assertSpyCallArgs(this.fn, 0, [arg, arg2]);
});
});

describe("arity 3", function () {
interface Context {
fn: Fn2Spy<Arity3>;
}

const arg = "";
const arg2 = 0;
const arg3 = false;

beforeEach<Context>(function () {
this.fn = spy((arg1: string, arg2: number, arg3: boolean): string =>
arg1 + arg2 + arg3
);
});

it<Context>("should pass 0 argument", function () {
const fn = partial(this.fn);

assertEquals(fn(arg, arg2, arg3), arg + arg2 + arg3);
assertSpyCallArgs(this.fn, 0, [arg, arg2, arg3]);
});

it<Context>("should pass 1 argument", function () {
const fn = partial(this.fn, arg);

assertEquals(fn(arg2, arg3), arg + arg2 + arg3);
assertSpyCallArgs(this.fn, 0, [arg, arg2, arg3]);
});

it<Context>("should pass 2 argument", function () {
const fn = partial(this.fn, arg, arg2);

assertEquals(fn(arg3), arg + arg2 + arg3);
assertSpyCallArgs(this.fn, 0, [arg, arg2, arg3]);
});

it<Context>("should pass 3 argument", function () {
const fn = partial(this.fn, arg, arg2, arg3);

assertEquals(fn(), arg + arg2 + arg3);
assertSpyCallArgs(this.fn, 0, [arg, arg2, arg3]);
});
});
});

0 comments on commit 3379335

Please sign in to comment.