mirror of
https://github.com/toss/es-toolkit.git
synced 2025-01-07 16:59:26 +03:00
feat(curry): add curry
function (#187)
* feat. Add utility types * feat. Add curry function * feat. add error throw * feat. add spec of curry * fix. fix type error when optional parameter exist * fix. fix type when rest parameters exist * fix. fix types error * fix. fix all type errors * refactor. rename OptionalStartIdx * test. add test codes when rest parameters exist * feat. add method to flexibleCurry * feat. fix all type errors and functional errors * chore. add benchmark * chore. fix comments * refactor. enhance type of flexibleCurry * refactor. refactor arrow function -> normal function * feat. correctly infer the errored type when the run method is called while the argument to be received is not optional * docs. add docs about curry * refactor. file refactor naming case of types * fix. fix types error caused by renaming of case * fix. fix errors same as last commit * fix. fix errors same as last commit * feat. keep curry function and type simple * fix. fix test codes with updated implementation * Update src/function/index.ts * feat(curry): Fix curry types * feat(curry): Fix curry types * feat(curry): Fix curry types * Remove ramda --------- Co-authored-by: Sojin Park <raon0211@toss.im> Co-authored-by: Sojin Park <raon0211@gmail.com>
This commit is contained in:
parent
0c9de4747a
commit
9364390715
15
benchmarks/curry.bench.ts
Normal file
15
benchmarks/curry.bench.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { bench, describe } from 'vitest';
|
||||
import { curry as curryToolkit } from 'es-toolkit';
|
||||
import { curry as curryLodash } from 'lodash';
|
||||
|
||||
describe('curry', () => {
|
||||
const fn = (a: number, b: string, c: boolean) => ({ a, b, c });
|
||||
|
||||
bench('es-toolkit/curry', () => {
|
||||
curryToolkit(fn)(1)('a')(true);
|
||||
});
|
||||
|
||||
bench('lodash/curry', () => {
|
||||
curryLodash(fn)(1)('a')(true);
|
||||
});
|
||||
});
|
42
docs/ja/reference/function/curry.md
Normal file
42
docs/ja/reference/function/curry.md
Normal file
@ -0,0 +1,42 @@
|
||||
# curry
|
||||
|
||||
関数をカリー化し、1回に1つのパラメータで呼び出すことができ、次のパラメータを取る新しい関数を返し続けます。
|
||||
このプロセスはすべてのパラメータが提供されるまで続き、すべてのパラメータが蓄積された段階で元の関数が呼び出されます。
|
||||
|
||||
## インターフェース
|
||||
```typescript
|
||||
function curry<R>(func: () => R): () => R;
|
||||
function curry<P, R>(func: (p: P) => R): (p: P) => R;
|
||||
function curry<P1, P2, R>(func: (p1: P1, p2: P2) => R): (p1: P1) => (p2: P2) => R;
|
||||
function curry<P1, P2, P3, R>(func: (p1: P1, p2: P2, p3: P3) => R): (p1: P1) => (p2: P2) => (p3: P3) => R;
|
||||
function curry<P1, P2, P3, P4, R>(func: (p1: P1, p2: P2, p3: P3, p4: P4) => R): (p1: P1) => (p2: P2) => (p3: P3) => (p4: P4) => R;
|
||||
function curry<P1, P2, P3, P4, P5, R>(func: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => R): (p1: P1) => (p2: P2) => (p3: P3) => (p4: P4) => (p5: P5) => R;
|
||||
function curry(func: (...args: any[]) => any): (...args: any[]) => any;
|
||||
function curry(func: (...args: any[]) => any): (...args: any[]) => any;
|
||||
```
|
||||
|
||||
### パラメータ
|
||||
|
||||
- `func` (`(...args: any[]) => any`): カリー化する関数。
|
||||
|
||||
### 戻り値
|
||||
|
||||
(`(...args: any[]) => any`): 1回に1つのパラメータで呼び出すことができるカリー化された関数。
|
||||
|
||||
## 例
|
||||
```typescript
|
||||
function sum(a: number, b: number, c: number) {
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
const curriedSum = curry(sum);
|
||||
|
||||
// パラメータ `a` に値 `10` を与えます。
|
||||
const sum10 = curriedSum(10);
|
||||
|
||||
// パラメータ `b` に値 `15` を与えます。
|
||||
const sum25 = sum10(15);
|
||||
|
||||
// パラメータ `c` に値 `5` を与えます。
|
||||
// 関数 'sum' はすべてのパラメータを受け取ったので、今値を返します。
|
||||
const result = sum25(5);
|
44
docs/ko/reference/function/curry.md
Normal file
44
docs/ko/reference/function/curry.md
Normal file
@ -0,0 +1,44 @@
|
||||
# curry
|
||||
|
||||
함수를 커링하여 한 번에 하나의 파라미터로 호출할 수 있도록 하고, 다음 파라미터를 받는 새로운 함수를 반환해요.
|
||||
모든 파라미터가 제공되면, 이때 원래 함수가 지금까지 주어진 파라미터로 호출돼요.
|
||||
|
||||
## 인터페이스
|
||||
|
||||
```typescript
|
||||
function curry<R>(func: () => R): () => R;
|
||||
function curry<P, R>(func: (p: P) => R): (p: P) => R;
|
||||
function curry<P1, P2, R>(func: (p1: P1, p2: P2) => R): (p1: P1) => (p2: P2) => R;
|
||||
function curry<P1, P2, P3, R>(func: (p1: P1, p2: P2, p3: P3) => R): (p1: P1) => (p2: P2) => (p3: P3) => R;
|
||||
function curry<P1, P2, P3, P4, R>(func: (p1: P1, p2: P2, p3: P3, p4: P4) => R): (p1: P1) => (p2: P2) => (p3: P3) => (p4: P4) => R;
|
||||
function curry<P1, P2, P3, P4, P5, R>(func: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => R): (p1: P1) => (p2: P2) => (p3: P3) => (p4: P4) => (p5: P5) => R;
|
||||
function curry(func: (...args: any[]) => any): (...args: any[]) => any;
|
||||
```
|
||||
|
||||
### 파라미터
|
||||
|
||||
- `func` (`(...args: any[]) => any`): 커링할 함수예요.
|
||||
|
||||
### 반환 값
|
||||
|
||||
(`(...args: any[]) => any`): 한 번에 하나의 파라미터로 호출할 수 있는 커링된 함수예요.
|
||||
|
||||
## 예시
|
||||
|
||||
```typescript
|
||||
function sum(a: number, b: number, c: number) {
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
const curriedSum = curry(sum);
|
||||
|
||||
// 파라미터 `a`로 값 `10`을 제공해요.
|
||||
const sum10 = curriedSum(10);
|
||||
|
||||
// 파라미터 `b`로 값 `15`을 제공해요.
|
||||
const sum25 = sum10(15);
|
||||
|
||||
// 파라미터 `c`로 값 `5`을 제공해요.
|
||||
// 함수 'sum'은 모든 파라미터를 받았기 때문에, 이제 값을 반환해요.
|
||||
const result = sum25(5);
|
||||
```
|
43
docs/reference/function/curry.md
Normal file
43
docs/reference/function/curry.md
Normal file
@ -0,0 +1,43 @@
|
||||
# curry
|
||||
|
||||
Curries a function, allowing it to be called with a single argument at a time and returning a new function that takes the next argument.
|
||||
This process continues until all arguments have been provided, at which point the original function is called with all accumulated arguments.
|
||||
|
||||
## Signature
|
||||
|
||||
```typescript
|
||||
function curry<R>(func: () => R): () => R;
|
||||
function curry<P, R>(func: (p: P) => R): (p: P) => R;
|
||||
function curry<P1, P2, R>(func: (p1: P1, p2: P2) => R): (p1: P1) => (p2: P2) => R;
|
||||
function curry<P1, P2, P3, R>(func: (p1: P1, p2: P2, p3: P3) => R): (p1: P1) => (p2: P2) => (p3: P3) => R;
|
||||
function curry<P1, P2, P3, P4, R>(func: (p1: P1, p2: P2, p3: P3, p4: P4) => R): (p1: P1) => (p2: P2) => (p3: P3) => (p4: P4) => R;
|
||||
function curry<P1, P2, P3, P4, P5, R>(func: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => R): (p1: P1) => (p2: P2) => (p3: P3) => (p4: P4) => (p5: P5) => R;
|
||||
function curry(func: (...args: any[]) => any): (...args: any[]) => any;
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
- `func` (`Function`): The function to curry.
|
||||
|
||||
### Returns
|
||||
|
||||
(`Function`): A curried function that can be called with a single argument at a time.
|
||||
|
||||
## Examples
|
||||
|
||||
```typescript
|
||||
function sum(a: number, b: number, c: number) {
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
const curriedSum = curry(sum);
|
||||
|
||||
// The parameter `a` should be given the value `10`.
|
||||
const sum10 = curriedSum(10);
|
||||
|
||||
// The parameter `b` should be given the value `15`.
|
||||
const sum25 = sum10(15);
|
||||
|
||||
// The parameter `c` should be given the value `5`. The function 'sum' has received all its arguments and will now return a value.
|
||||
const result = sum25(5);
|
||||
```
|
42
docs/zh_hans/reference/function/curry.md
Normal file
42
docs/zh_hans/reference/function/curry.md
Normal file
@ -0,0 +1,42 @@
|
||||
# curry
|
||||
|
||||
将一个函数柯里化,允许它每次只用一个参数调用,并返回一个接受下一个参数的新函数。这个过程会继续,直到所有参数都已提供,此时将使用所有累积的参数调用原始函数。
|
||||
|
||||
## 签名
|
||||
|
||||
```typescript
|
||||
function curry<R>(func: () => R): () => R;
|
||||
function curry<P, R>(func: (p: P) => R): (p: P) => R;
|
||||
function curry<P1, P2, R>(func: (p1: P1, p2: P2) => R): (p1: P1) => (p2: P2) => R;
|
||||
function curry<P1, P2, P3, R>(func: (p1: P1, p2: P2, p3: P3) => R): (p1: P1) => (p2: P2) => (p3: P3) => R;
|
||||
function curry<P1, P2, P3, P4, R>(func: (p1: P1, p2: P2, p3: P3, p4: P4) => R): (p1: P1) => (p2: P2) => (p3: P3) => (p4: P4) => R;
|
||||
function curry<P1, P2, P3, P4, P5, R>(func: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => R): (p1: P1) => (p2: P2) => (p3: P3) => (p4: P4) => (p5: P5) => R;
|
||||
function curry(func: (...args: any[]) => any): (...args: any[]) => any;
|
||||
```
|
||||
|
||||
### 参数
|
||||
|
||||
- `func` (`(...args: any[]) => any`): 要进行柯里化的函数。
|
||||
|
||||
### 返回值
|
||||
|
||||
(`(...args: any[]) => any`): 一个可以每次调用一个参数的柯里化函数。
|
||||
|
||||
## 示例
|
||||
|
||||
```typescript
|
||||
function sum(a: number, b: number, c: number) {
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
const curriedSum = curry(sum);
|
||||
|
||||
// The parameter `a` should be given the value `10`.
|
||||
const sum10 = curriedSum(10);
|
||||
|
||||
// The parameter `b` should be given the value `15`.
|
||||
const sum25 = sum10(15);
|
||||
|
||||
// The parameter `c` should be given the value `5`. The function 'sum' has received all its arguments and will now return a value.
|
||||
const result = sum25(5);
|
||||
```
|
@ -171,4 +171,4 @@
|
||||
"format": "prettier --write .",
|
||||
"transform": "jscodeshift -t ./.scripts/tests/transform-lodash-test.ts $0 && prettier --write $0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
31
src/function/curry.spec.ts
Normal file
31
src/function/curry.spec.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { describe, expect, expectTypeOf, it } from 'vitest';
|
||||
import { curry } from './curry';
|
||||
|
||||
describe('curry', () => {
|
||||
it('should return original type of function when function without any arguments received', () => {
|
||||
const fn = () => 'test';
|
||||
const curried = curry(fn);
|
||||
|
||||
expect(curried()).toBe('test');
|
||||
});
|
||||
|
||||
it('should curry based on the number of arguments given', () => {
|
||||
const fn = (a: number, b: number, c: number, d: number) => [a, b, c, d];
|
||||
const curried = curry(fn);
|
||||
const expected = [1, 2, 3, 4];
|
||||
|
||||
expect(curried(1)(2)(3)(4)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should inference type correctly', () => {
|
||||
const fn = (a: number, b: string, c: boolean) => ({ a, b, c });
|
||||
const curried = curry(fn);
|
||||
|
||||
expectTypeOf(curried).parameters.toEqualTypeOf<[number]>();
|
||||
expectTypeOf(curried(1)).parameters.toEqualTypeOf<[string]>();
|
||||
expectTypeOf(curried(1)).not.toEqualTypeOf<{ a: number; b: string; c: boolean }>();
|
||||
expectTypeOf(curried(1)('a')).parameters.toEqualTypeOf<[boolean]>();
|
||||
expectTypeOf(curried(1)('a')).not.toEqualTypeOf<{ a: number; b: string; c: boolean }>();
|
||||
expectTypeOf(curried(1)('a')(true)).toEqualTypeOf<{ a: number; b: string; c: boolean }>();
|
||||
});
|
||||
});
|
180
src/function/curry.ts
Normal file
180
src/function/curry.ts
Normal file
@ -0,0 +1,180 @@
|
||||
/**
|
||||
* Curries a function, allowing it to be called with a single argument at a time and returning a new function that takes the next argument.
|
||||
* This process continues until all arguments have been provided, at which point the original function is called with all accumulated arguments.
|
||||
*
|
||||
* @param {() => R} func - The function to curry.
|
||||
* @returns {() => R} A curried function.
|
||||
*
|
||||
* @example
|
||||
* function noArgFunc() {
|
||||
* return 42;
|
||||
* }
|
||||
* const curriedNoArgFunc = curry(noArgFunc);
|
||||
* console.log(curriedNoArgFunc()); // 42
|
||||
*/
|
||||
export function curry<R>(func: () => R): () => R;
|
||||
|
||||
/**
|
||||
* Curries a function, allowing it to be called with a single argument at a time and returning a new function that takes the next argument.
|
||||
* This process continues until all arguments have been provided, at which point the original function is called with all accumulated arguments.
|
||||
*
|
||||
* @param {(p: P) => R} func - The function to curry.
|
||||
* @returns {(p: P) => R} A curried function.
|
||||
*
|
||||
* @example
|
||||
* function oneArgFunc(a: number) {
|
||||
* return a * 2;
|
||||
* }
|
||||
* const curriedOneArgFunc = curry(oneArgFunc);
|
||||
* console.log(curriedOneArgFunc(5)); // 10
|
||||
*/
|
||||
export function curry<P, R>(func: (p: P) => R): (p: P) => R;
|
||||
|
||||
/**
|
||||
* Curries a function, allowing it to be called with a single argument at a time and returning a new function that takes the next argument.
|
||||
* This process continues until all arguments have been provided, at which point the original function is called with all accumulated arguments.
|
||||
*
|
||||
* @param {(p1: P1, p2: P2) => R} func - The function to curry.
|
||||
* @returns {(p1: P1) => (p2: P2) => R} A curried function.
|
||||
*
|
||||
* @example
|
||||
* function twoArgFunc(a: number, b: number) {
|
||||
* return a + b;
|
||||
* }
|
||||
* const curriedTwoArgFunc = curry(twoArgFunc);
|
||||
* const add5 = curriedTwoArgFunc(5);
|
||||
* console.log(add5(10)); // 15
|
||||
*/
|
||||
export function curry<P1, P2, R>(func: (p1: P1, p2: P2) => R): (p1: P1) => (p2: P2) => R;
|
||||
|
||||
/**
|
||||
* Curries a function, allowing it to be called with a single argument at a time and returning a new function that takes the next argument.
|
||||
* This process continues until all arguments have been provided, at which point the original function is called with all accumulated arguments.
|
||||
*
|
||||
* @param {(p1: P1, p2: P2, p3: P3, p4: P4) => R} func - The function to curry.
|
||||
* @returns {(p1: P1) => (p2: P2) => (p3: P3) => (p4: P4) => R} A curried function.
|
||||
*
|
||||
* @example
|
||||
* function fourArgFunc(a: number, b: number, c: number, d: number) {
|
||||
* return a + b + c + d;
|
||||
* }
|
||||
* const curriedFourArgFunc = curry(fourArgFunc);
|
||||
* const add1 = curriedFourArgFunc(1);
|
||||
* const add2 = add1(2);
|
||||
* const add3 = add2(3);
|
||||
* console.log(add3(4)); // 10
|
||||
*/
|
||||
export function curry<P1, P2, P3, R>(func: (p1: P1, p2: P2, p3: P3) => R): (p1: P1) => (p2: P2) => (p3: P3) => R;
|
||||
|
||||
/**
|
||||
* Curries a function, allowing it to be called with a single argument at a time and returning a new function that takes the next argument.
|
||||
* This process continues until all arguments have been provided, at which point the original function is called with all accumulated arguments.
|
||||
*
|
||||
* @param {(p1: P1, p2: P2, p3: P3, p4: P4) => R} func - The function to curry.
|
||||
* @returns {(p1: P1) => (p2: P2) => (p3: P3) => (p4: P4) => R} A curried function.
|
||||
*
|
||||
* @example
|
||||
* function fourArgFunc(a: number, b: number, c: number, d: number) {
|
||||
* return a + b + c + d;
|
||||
* }
|
||||
* const curriedFourArgFunc = curry(fourArgFunc);
|
||||
* const add1 = curriedFourArgFunc(1);
|
||||
* const add2 = add1(2);
|
||||
* const add3 = add2(3);
|
||||
* console.log(add3(4)); // 10
|
||||
*/
|
||||
export function curry<P1, P2, P3, P4, R>(
|
||||
func: (p1: P1, p2: P2, p3: P3, p4: P4) => R
|
||||
): (p1: P1) => (p2: P2) => (p3: P3) => (p4: P4) => R;
|
||||
|
||||
/**
|
||||
* Curries a function, allowing it to be called with a single argument at a time and returning a new function that takes the next argument.
|
||||
* This process continues until all arguments have been provided, at which point the original function is called with all accumulated arguments.
|
||||
*
|
||||
* @param {(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => R} func - The function to curry.
|
||||
* @returns {(p1: P1) => (p2: P2) => (p3: P3) => (p4: P4) => (p5: P5) => R} A curried function.
|
||||
*
|
||||
* @example
|
||||
* function fiveArgFunc(a: number, b: number, c: number, d: number, e: number) {
|
||||
* return a + b + c + d + e;
|
||||
* }
|
||||
* const curriedFiveArgFunc = curry(fiveArgFunc);
|
||||
* const add1 = curriedFiveArgFunc(1);
|
||||
* const add2 = add1(2);
|
||||
* const add3 = add2(3);
|
||||
* const add4 = add3(4);
|
||||
* console.log(add4(5)); // 15
|
||||
*/
|
||||
export function curry<P1, P2, P3, P4, P5, R>(
|
||||
func: (p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => R
|
||||
): (p1: P1) => (p2: P2) => (p3: P3) => (p4: P4) => (p5: P5) => R;
|
||||
|
||||
/**
|
||||
* Curries a function, allowing it to be called with a single argument at a time and returning a new function that takes the next argument.
|
||||
* This process continues until all arguments have been provided, at which point the original function is called with all accumulated arguments.
|
||||
*
|
||||
* @param {(...args: any[]) => any} func - The function to curry.
|
||||
* @returns {(...args: any[]) => any} A curried function that can be called with a single argument at a time.
|
||||
*
|
||||
* @example
|
||||
* function sum(a: number, b: number, c: number) {
|
||||
* return a + b + c;
|
||||
* }
|
||||
*
|
||||
* const curriedSum = curry(sum);
|
||||
*
|
||||
* // The parameter `a` should be given the value `10`.
|
||||
* const sum10 = curriedSum(10);
|
||||
*
|
||||
* // The parameter `b` should be given the value `15`.
|
||||
* const sum25 = sum10(15);
|
||||
*
|
||||
* // The parameter `c` should be given the value `5`. The function 'sum' has received all its arguments and will now return a value.
|
||||
* const result = sum25(5);
|
||||
*/
|
||||
export function curry(func: (...args: any[]) => any): (...args: any[]) => any;
|
||||
|
||||
/**
|
||||
* Curries a function, allowing it to be called with a single argument at a time and returning a new function that takes the next argument.
|
||||
* This process continues until all arguments have been provided, at which point the original function is called with all accumulated arguments.
|
||||
*
|
||||
* @param {(...args: any[]) => any} func - The function to curry.
|
||||
* @returns {(...args: any[]) => any} A curried function that can be called with a single argument at a time.
|
||||
*
|
||||
* @example
|
||||
* function sum(a: number, b: number, c: number) {
|
||||
* return a + b + c;
|
||||
* }
|
||||
*
|
||||
* const curriedSum = curry(sum);
|
||||
*
|
||||
* // The parameter `a` should be given the value `10`.
|
||||
* const sum10 = curriedSum(10);
|
||||
*
|
||||
* // The parameter `b` should be given the value `15`.
|
||||
* const sum25 = sum10(15);
|
||||
*
|
||||
* // The parameter `c` should be given the value `5`. The function 'sum' has received all its arguments and will now return a value.
|
||||
* const result = sum25(5);
|
||||
*/
|
||||
export function curry(func: (...args: any[]) => any): (...args: any[]) => any {
|
||||
if (func.length === 0 || func.length === 1) {
|
||||
return func;
|
||||
}
|
||||
|
||||
return function (arg: any) {
|
||||
return makeCurry(func, func.length, [arg]);
|
||||
} as any;
|
||||
}
|
||||
|
||||
function makeCurry<F extends (...args: any) => any>(origin: F, argsLength: number, args: any[]) {
|
||||
if (args.length === argsLength) {
|
||||
return origin(...args);
|
||||
} else {
|
||||
const next = function (arg: Parameters<F>[0]) {
|
||||
return makeCurry(origin, argsLength, [...args, arg]);
|
||||
};
|
||||
|
||||
return next as any;
|
||||
}
|
||||
}
|
@ -11,4 +11,5 @@ export { unary } from './unary.ts';
|
||||
export { partial } from './partial.ts';
|
||||
export { partialRight } from './partialRight.ts';
|
||||
export { rest } from './rest.ts';
|
||||
export { curry } from './curry.ts';
|
||||
export { spread } from './spread.ts';
|
||||
|
Loading…
Reference in New Issue
Block a user