10000 :sparkles: Add curry function · TomokiMiyauci/fonction@1091e30 · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Commit 1091e30

Browse files
committed
✨ Add curry function
Closes #96
1 parent 948d5ff commit 1091e30

File tree

6 files changed

+204
-38
lines changed
  • src
  • test

6 files changed

+204
-38
lines changed

api.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,7 @@ const api: Api = {
163163
rambda: 'converge',
164164
fonction: undefined
165165
},
166-
curry: {
167-
ramda: 'curry',
168-
rambda: 'curry',
169-
fonction: undefined
170-
},
166+
curry: ['lodash', 'rambda', 'ramda'],
171167
defaultTo: ['rambda', 'ramda'],
172168
drop: {
173169
ramda: 'drop',

mod.ts

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export { append } from './src/append.ts'
66
export { chunk } from './src/chunk.ts'
77
export { _ } from './src/constants/index.ts'
88
export { constructorName } from './src/constructorName.ts'
9+
export { curry } from './src/curry.ts'
910
export { dec } from './src/dec.ts'
1011
export { defaultTo } from './src/defaultTo.ts'
1112
export { divide } from './src/divide.ts'

src/_/curry2.ts

-24
This file was deleted.

src/curry.ts

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright 2021-present the Fonction authors. All rights reserved. MIT license.
2+
import { gte } from './gte.ts'
3+
import { ifElse } from './ifElse.ts'
4+
import { length } from './length.ts'
5+
6+
type UnionToIntersection<U> = (
7+
U extends unknown ? (arg: U) => void : never
8+
) extends (arg: infer I) => void
9+
? I
10+
: never
11+
12+
type Tuple = readonly unknown[]
13+
14+
/**
15+
* @internal
16+
*
17+
* @example
18+
* ```ts
19+
* Pop<[1, 2, 3]> = [1, 2].
20+
* ```
21+
*/
22+
type Pop<T extends Tuple> = T extends [...infer Head, infer _] ? Head : never
23+
24+
/**
25+
* @example
26+
* ```ts
27+
* Shift<[1], [1, 2, 3]> = [2, 3].
28+
* Shift<[1, 2], [1, 2, 3]> = [3].
29+
* ```
30+
*/
31+
type Shift<Shifted extends Tuple, T extends Tuple> = T extends [
32+
...Shifted,
33+
...infer Rest
34+
]
35+
? Rest
36+
: never
37+
38+
/**
39+
* @example
40+
* ```ts
41+
* Slices<[1, 2, 3]> = [1] | [1, 2] | [1, 2, 3].
42+
* ```
43+
*/
44+
type Slices<T extends Tuple> = T extends [] ? never : T | Slices<Pop<T>>
45+
46+
/**
47+
* @example
48+
* ```ts
49+
* OverloadsByArgs<[1] | [1, 2], [1, 2, 3], 7> =
50+
* | CurriedWithFixArgs<[1], [2, 3], 7>
51+
* | CurriedWithFixArgs<[1, 2], [3], 7>.
52+
* ```
53+
*/
54+
type OverloadsByArgs<
55+
Args extends Tuple,
56+
FullArgs extends Tuple,
57+
ReturnValue
58+
> = Args extends unknown
59+
? CurriedWithFixArgs<Args, Shift<Args, FullArgs>, ReturnValue>
60+
: never
61+
62+
type CurriedWithFixArgs<
63+
Args extends Tuple,
64+
RestArgs extends Tuple,
65+
ReturnValue
66+
> = (...args: Args) => Curried<RestArgs, ReturnValue>
67+
68+
type Curried<Args extends Tuple, ReturnValue> = Args extends []
69+
? ReturnValue
70+
: UnionToIntersection<OverloadsByArgs<Slices<Args>, Args, ReturnValue>>
71+
72+
/**
73+
* Creates a function that accepts arguments of `fn` and either invokes `fn` returning its result, if at least arity number of arguments have been provided, or returns a function that accepts the remaining `fn` arguments, and so on.
74+
*
75+
* @param fn - The function to curry
76+
* @returns The new curried function
77+
*
78+
* @remarks
79+
* Maximum number of arity is 16. Beyond that, the type system will breaks.
80+
*
81+
* @example
82+
* ```ts
83+
* const replace = (from: string, to: string, val: string) => val.replace(from, to)
84+
* const curriedReplace = curry(replace)
85+
* const curriedReplace('hello', 'hi', 'hello world') // 'hi world'
86+
* const curriedReplace('hello')('hi', 'hello world') // 'hi world'
87+
* const curriedReplace('hello')('hi')('hello world') // 'hi world'
88+
* ```
89+
*
90+
* @beta
91+
*/
92+
const curry = <T extends unknown[], R>(
93+
fn: (...args: T) => R
94+
): T['length'] extends 0 ? () => R : Curried<T, R> => {
95+
const curried: any = (...t: T) =>
96+
ifElse(
97+
gte(length(t), length(fn)),
98+
() => fn(...t),
99+
() => curried.bind(null, ...t)
100+
)
101+
102+
return curried
103+
}
104+
105+
export { curry }

test/_/curry2.test.ts

-9
This file was deleted.

test/curry.test.ts

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Copyright 2021-present the Fonction authors. All rights reserved. MIT license.
2+
import { assertEquals } from '../dev_deps.ts'
3+
import { curry } from '../src/curry.ts'
4+
import { assertEqual } from './asserts.ts'
5+
const arity0 = () => true
6+
const arity1 = (a: unknown) => a
7+
const arity2 = (a: unknown, b: unknown) => [a, b]
8+
const arity3 = (a: unknown, b: unknown, c: unknown) => [a, b, c]
9+
const arity4 = (a: unknown, b: unknown, c: unknown, d: unknown) => [a, b, c, d]
10+
const arity5 = (a: unknown, b: unknown, c: unknown, d: unknown, e: unknown) => [
11+
a,
12+
b,
13+
c,
14+
d,
15+
e
16+
]
17+
18+
Deno.test('curry', () => {
19+
const add = curry((a: number, b: number) => a + b)
20+
assertEquals(add(1, 2), 3)
21+
assertEquals(add(1)(2), 3)
22+
23+
assertEquals(curry(arity0)(), true)
24+
assertEquals(curry(arity1)(''), '')
25+
assertEquals(typeof curry(arity2)(''), 'function')
26+
assertEquals(curry(arity2)('', 1), ['', 1])
27+
assertEquals(curry(arity2)('')(1), ['', 1])
28+
29+
assertEquals(typeof curry(arity3)(''), 'function')
30+
assertEquals(typeof curry(arity3)('')(1), 'function')
31+
assertEquals(curry(arity3)('')(1)(false), ['', 1, false])
32+
assertEquals(curry(arity3)('', 1)(false), ['', 1, false])
33+
assertEquals(curry(arity3)('', 1, false), ['', 1, false])
34+
35+
assertEquals(curry(arity4)('')(1)(false)(true), ['', 1, false, true])
36+
assertEquals(curry(arity4)('', 1)(false)(true), ['', 1, false, true])
37+
assertEquals(curry(arity4)('', 1, false)(true), ['', 1, false, true])
38+
assertEquals(curry(arity4)('', 1, false, true), ['', 1, false, true])
39+
40+
assertEquals(curry(arity5)('')(1)(false)(true)(0n), ['', 1, false, true, 0n])
41+
assertEquals(curry(arity5)('', 1)(false)(true)(0n), ['', 1, false, true, 0n])
42+
assertEquals(curry(arity5)('', 1, false)(true)(0n), ['', 1, false, true, 0n])
43+
assertEquals(curry(arity5)('', 1, false, true)(0n), ['', 1, false, true, 0n])
44+
assertEquals(curry(arity5)('', 1, false, true, 0n), ['', 1, false, true, 0n])
45+
assertEquals(curry(arity5)('')(1, false, true, 0n), ['', 1, false, true, 0n])
46+
assertEquals(curry(arity5)('')(1)(false, true, 0n), ['', 1, false, true, 0n])
47+
assertEquals(curry(arity5)('')(1)(false)(true, 0n), ['', 1, false, true, 0n])
48+
49+
assertEqual<() => boolean>(curry(arity0))
50+
assertEqual<unknown>(curry(arity1)(''))
51+
assertEqual<unknown[]>(curry(arity2)('')(''))
52+
assertEqual<unknown[]>(curry(arity2)('', ''))
53+
assertEqual<unknown[]>(curry(arity3)('', '', ''))
54+
assertEqual<unknown[]>(curry(arity3)('', '', ''))
55+
assertEqual<unknown[]>(curry(arity3)('')('')(''))
56+
assertEqual<unknown[]>(
57+
curry(arityMax)(
58+
'',
59+
'',
60+
'',
61+
'',
62+
'',
63+
'',
64+
'',
65+
'',
66+
'',
67+
'',
68+
'',
69+
'',
70+
'',
71+
'',
72+
'',
73+
'',
74+
''
75+
)
76+
)
77+
})
78+
79+
const arityMax = (
80+
a: unknown,
81+
b: unknown,
82+
c: unknown,
83+
d: unknown,
84+
e: unknown,
85+
f: unknown,
86+
g: unknown,
87+
h: unknown,
88+
i: unknown,
89+
j: unknown,
90+
k: unknown,
91+
l: unknown,
92+
m: unknown,
93+
n: unknown,
94+
o: unknown,
95+
p: unknown,
96+
q: unknown
97+
) => [a, b, c, d, e]

0 commit comments

Comments
 (0)
0