1import { dual } from "../Function.ts"
2import type { Kind, TypeLambda } from "../HKT.ts"
3import type { NoInfer } from "../Types.ts"
4
5interface Map<F extends TypeLambda> {
6 <A, B>(
7 f: (a: A) => B
8 ): <R, O, E>(self: Kind<F, R, O, E, A>) => Kind<F, R, O, E, B>
9 <R, O, E, A, B>(
10 self: Kind<F, R, O, E, A>,
11 f: (a: A) => B
12 ): Kind<F, R, O, E, B>
13}
14
15interface FlatMap<F extends TypeLambda> {
16 <A, R2, O2, E2, B>(
17 f: (a: A) => Kind<F, R2, O2, E2, B>
18 ): <R1, O1, E1>(
19 self: Kind<F, R1, O1, E1, A>
20 ) => Kind<F, R1 & R2, O1 | O2, E1 | E2, B>
21 <R1, O1, E1, A, R2, O2, E2, B>(
22 self: Kind<F, R1, O1, E1, A>,
23 f: (a: A) => Kind<F, R2, O2, E2, B>
24 ): Kind<F, R1 & R2, O1 | O2, E1 | E2, B>
25}
26
27/** @internal */
28export const let_ = <F extends TypeLambda>(
29 map: Map<F>
30): {
31 <N extends string, A extends object, B>(
32 name: Exclude<N, keyof A>,
33 f: (a: NoInfer<A>) => B
34 ): <R, O, E>(
35 self: Kind<F, R, O, E, A>
36 ) => Kind<F, R, O, E, { [K in keyof A | N]: K extends keyof A ? A[K] : B }>
37 <R, O, E, A extends object, N extends string, B>(
38 self: Kind<F, R, O, E, A>,
39 name: Exclude<N, keyof A>,
40 f: (a: NoInfer<A>) => B
41 ): Kind<F, R, O, E, { [K in keyof A | N]: K extends keyof A ? A[K] : B }>
42} =>
43 dual(
44 3,
45 <R, O, E, A extends object, N extends string, B>(
46 self: Kind<F, R, O, E, A>,
47 name: Exclude<N, keyof A>,
48 f: (a: NoInfer<A>) => B
49 ): Kind<F, R, O, E, { [K in keyof A | N]: K extends keyof A ? A[K] : B }> =>
50 map(self, (a) => ({ ...a, [name]: f(a) }) as any)
51 )
52
53/** @internal */
54export const bindTo = <F extends TypeLambda>(
55 map: Map<F>
56): {
57 <N extends string>(
58 name: N
59 ): <R, O, E, A>(
60 self: Kind<F, R, O, E, A>
61 ) => Kind<F, R, O, E, Record<N, A>>
62 <R, O, E, A, N extends string>(
63 self: Kind<F, R, O, E, A>,
64 name: N
65 ): Kind<F, R, O, E, Record<N, A>>
66} =>
67 dual(
68 2,
69 <R, O, E, A, N extends string>(
70 self: Kind<F, R, O, E, A>,
71 name: N
72 ): Kind<F, R, O, E, Record<N, A>> => map(self, (a) => ({ [name]: a }) as Record<N, A>)
73 )
74
75/** @internal */
76export const bind = <F extends TypeLambda>(
77 map: Map<F>,
78 flatMap: FlatMap<F>
79): {
80 <N extends string, A extends object, R2, O2, E2, B>(
81 name: Exclude<N, keyof A>,
82 f: (a: NoInfer<A>) => Kind<F, R2, O2, E2, B>
83 ): <R1, O1, E1>(
84 self: Kind<F, R1, O1, E1, A>
85 ) => Kind<
86 F,
87 R1 & R2,
88 O1 | O2,
89 E1 | E2,
90 { [K in keyof A | N]: K extends keyof A ? A[K] : B }
91 >
92 <R1, O1, E1, A extends object, N extends string, R2, O2, E2, B>(
93 self: Kind<F, R1, O1, E1, A>,
94 name: Exclude<N, keyof A>,
95 f: (a: NoInfer<A>) => Kind<F, R2, O2, E2, B>
96 ): Kind<
97 F,
98 R1 & R2,
99 O1 | O2,
100 E1 | E2,
101 { [K in keyof A | N]: K extends keyof A ? A[K] : B }
102 >
103} =>
104 dual(
105 3,
106 <R1, O1, E1, A, N extends string, R2, O2, E2, B>(
107 self: Kind<F, R1, O1, E1, A>,
108 name: Exclude<N, keyof A>,
109 f: (a: NoInfer<A>) => Kind<F, R2, O2, E2, B>
110 ): Kind<
111 F,
112 R1 & R2,
113 O1 | O2,
114 E1 | E2,
115 { [K in keyof A | N]: K extends keyof A ? A[K] : B }
116 > => flatMap(self, (a) => map(f(a), (b) => ({ ...a, [name]: b }) as any))
117 )