@fncts/io 0.0.9 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. package/Channel/api.d.ts +27 -4
  2. package/Channel/internal/MergeDecision.d.ts +14 -0
  3. package/IO/runtime.d.ts +8 -1
  4. package/STM/api.d.ts +7 -0
  5. package/Sink/api.d.ts +637 -8
  6. package/TReentrantLock/api.d.ts +97 -0
  7. package/TReentrantLock/definition.d.ts +65 -0
  8. package/TReentrantLock.d.ts +2 -0
  9. package/_cjs/Cached/api.cjs +1 -1
  10. package/_cjs/Cached/api.cjs.map +1 -1
  11. package/_cjs/Channel/api/mapOutIOC.cjs +1 -1
  12. package/_cjs/Channel/api/mapOutIOC.cjs.map +1 -1
  13. package/_cjs/Channel/api/mergeAllWith.cjs +2 -2
  14. package/_cjs/Channel/api/mergeAllWith.cjs.map +1 -1
  15. package/_cjs/Channel/api/mergeWith.cjs +1 -1
  16. package/_cjs/Channel/api/mergeWith.cjs.map +1 -1
  17. package/_cjs/Channel/api.cjs +87 -32
  18. package/_cjs/Channel/api.cjs.map +1 -1
  19. package/_cjs/Channel/internal/MergeDecision.cjs +11 -2
  20. package/_cjs/Channel/internal/MergeDecision.cjs.map +1 -1
  21. package/_cjs/IO/api/foreachC.cjs +2 -2
  22. package/_cjs/IO/api/foreachC.cjs.map +1 -1
  23. package/_cjs/IO/runtime.cjs +18 -7
  24. package/_cjs/IO/runtime.cjs.map +1 -1
  25. package/_cjs/STM/api.cjs +15 -6
  26. package/_cjs/STM/api.cjs.map +1 -1
  27. package/_cjs/Sink/api.cjs +1475 -42
  28. package/_cjs/Sink/api.cjs.map +1 -1
  29. package/_cjs/Stream/api.cjs +29 -29
  30. package/_cjs/Stream/api.cjs.map +1 -1
  31. package/_cjs/TReentrantLock/api.cjs +297 -0
  32. package/_cjs/TReentrantLock/api.cjs.map +1 -0
  33. package/_cjs/TReentrantLock/definition.cjs +125 -0
  34. package/_cjs/TReentrantLock/definition.cjs.map +1 -0
  35. package/_cjs/TReentrantLock.cjs +32 -0
  36. package/_cjs/TReentrantLock.cjs.map +1 -0
  37. package/_cjs/collection/immutable/Conc/dropUntilIO.cjs +38 -0
  38. package/_cjs/collection/immutable/Conc/dropUntilIO.cjs.map +1 -0
  39. package/_cjs/collection/immutable/Conc/dropWhileIO.cjs +38 -0
  40. package/_cjs/collection/immutable/Conc/dropWhileIO.cjs.map +1 -0
  41. package/_cjs/collection/immutable/Conc/filterIO.cjs +35 -0
  42. package/_cjs/collection/immutable/Conc/filterIO.cjs.map +1 -0
  43. package/_cjs/collection/immutable/Conc.cjs +13 -0
  44. package/_cjs/collection/immutable/Conc.cjs.map +1 -1
  45. package/_cjs/demo.cjs +15 -0
  46. package/_cjs/demo.cjs.map +1 -0
  47. package/_mjs/Cached/api.mjs +1 -1
  48. package/_mjs/Cached/api.mjs.map +1 -1
  49. package/_mjs/Channel/api/mapOutIOC.mjs +1 -1
  50. package/_mjs/Channel/api/mapOutIOC.mjs.map +1 -1
  51. package/_mjs/Channel/api/mergeAllWith.mjs +2 -2
  52. package/_mjs/Channel/api/mergeAllWith.mjs.map +1 -1
  53. package/_mjs/Channel/api/mergeWith.mjs +1 -1
  54. package/_mjs/Channel/api/mergeWith.mjs.map +1 -1
  55. package/_mjs/Channel/api.mjs +75 -30
  56. package/_mjs/Channel/api.mjs.map +1 -1
  57. package/_mjs/Channel/internal/MergeDecision.mjs +7 -0
  58. package/_mjs/Channel/internal/MergeDecision.mjs.map +1 -1
  59. package/_mjs/IO/api/foreachC.mjs +2 -2
  60. package/_mjs/IO/api/foreachC.mjs.map +1 -1
  61. package/_mjs/IO/runtime.mjs +15 -5
  62. package/_mjs/IO/runtime.mjs.map +1 -1
  63. package/_mjs/STM/api.mjs +13 -6
  64. package/_mjs/STM/api.mjs.map +1 -1
  65. package/_mjs/Sink/api.mjs +1287 -37
  66. package/_mjs/Sink/api.mjs.map +1 -1
  67. package/_mjs/Stream/api.mjs +29 -29
  68. package/_mjs/Stream/api.mjs.map +1 -1
  69. package/_mjs/TReentrantLock/api.mjs +243 -0
  70. package/_mjs/TReentrantLock/api.mjs.map +1 -0
  71. package/_mjs/TReentrantLock/definition.mjs +102 -0
  72. package/_mjs/TReentrantLock/definition.mjs.map +1 -0
  73. package/_mjs/TReentrantLock.mjs +4 -0
  74. package/_mjs/TReentrantLock.mjs.map +1 -0
  75. package/_mjs/collection/immutable/Conc/dropUntilIO.mjs +26 -0
  76. package/_mjs/collection/immutable/Conc/dropUntilIO.mjs.map +1 -0
  77. package/_mjs/collection/immutable/Conc/dropWhileIO.mjs +26 -0
  78. package/_mjs/collection/immutable/Conc/dropWhileIO.mjs.map +1 -0
  79. package/_mjs/collection/immutable/Conc/filterIO.mjs +22 -0
  80. package/_mjs/collection/immutable/Conc/filterIO.mjs.map +1 -0
  81. package/_mjs/collection/immutable/Conc.mjs +1 -0
  82. package/_mjs/collection/immutable/Conc.mjs.map +1 -1
  83. package/_mjs/demo.mjs +7 -0
  84. package/_mjs/demo.mjs.map +1 -0
  85. package/_src/Channel/api.ts +98 -11
  86. package/_src/Channel/internal/MergeDecision.ts +15 -0
  87. package/_src/IO/api.ts +1 -1
  88. package/_src/IO/runtime.ts +18 -11
  89. package/_src/STM/api.ts +9 -0
  90. package/_src/Sink/api.ts +1725 -36
  91. package/_src/TFuture/definition.ts +1 -1
  92. package/_src/TReentrantLock/api.ts +193 -0
  93. package/_src/TReentrantLock/definition.ts +86 -0
  94. package/_src/TReentrantLock.ts +4 -0
  95. package/_src/collection/immutable/Conc/dropUntilIO.ts +24 -0
  96. package/_src/collection/immutable/Conc/dropWhileIO.ts +26 -0
  97. package/_src/collection/immutable/Conc/filterIO.ts +16 -0
  98. package/_src/collection/immutable/Conc.ts +1 -0
  99. package/_src/demo.ts +6 -0
  100. package/collection/immutable/Conc/dropUntilIO.d.ts +7 -0
  101. package/collection/immutable/Conc/dropWhileIO.d.ts +7 -0
  102. package/collection/immutable/Conc/filterIO.d.ts +7 -0
  103. package/collection/immutable/Conc.d.ts +1 -0
  104. package/demo.d.ts +1 -0
  105. package/package.json +3 -3
package/_src/Sink/api.ts CHANGED
@@ -1,18 +1,274 @@
1
- function collectLoop<Err, A>(state: Conc<A>): Channel<unknown, Err, Conc<A>, unknown, Err, Conc<never>, Conc<A>> {
2
- return Channel.readWithCause(
3
- (inp: Conc<A>) => collectLoop(state.concat(inp)),
4
- Channel.failCauseNow,
5
- () => Channel.endNow(state),
1
+ import { AtomicReference } from "@fncts/base/internal/AtomicReference";
2
+
3
+ import { MergeDecision } from "../Channel/internal/MergeDecision.js";
4
+
5
+ /**
6
+ * Like {@link zip}, but keeps only the result from this sink
7
+ *
8
+ * @tsplus fluent fncts.io.Sink apFirst
9
+ */
10
+ export function apFirst<R, E, In, L, Z, R1, E1, In1 extends In, L1 extends L, Z1>(
11
+ self: Sink<R, E, In, L, Z>,
12
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
13
+ ): Sink<R & R1, E | E1, In & In1, L | L1, Z> {
14
+ return self.zipWith(that, (z, _) => z);
15
+ }
16
+
17
+ /**
18
+ * Like {@link zipC}, but keeps only the result from this sink
19
+ *
20
+ * @tsplus fluent fncts.io.Sink apFirstC
21
+ */
22
+ export function apFirstC<R, E, In, L, Z, R1, E1, In1 extends In, L1 extends L, Z1>(
23
+ self: Sink<R, E, In, L, Z>,
24
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
25
+ ): Sink<R & R1, E | E1, In & In1, L | L1, Z> {
26
+ return self.zipWithC(that, (z, _) => z);
27
+ }
28
+
29
+ /**
30
+ * Like {@link zip}, but keeps only the result from the `that` sink
31
+ *
32
+ * @tsplus fluent fncts.io.Sink apSecond
33
+ */
34
+ export function apSecond<R, E, In, L, Z, R1, E1, In1 extends In, L1 extends L, Z1>(
35
+ self: Sink<R, E, In, L, Z>,
36
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
37
+ __tsplusTrace?: string,
38
+ ): Sink<R & R1, E | E1, In & In1, L | L1, Z1> {
39
+ return self.zipWith(that, (_, z1) => z1);
40
+ }
41
+
42
+ /**
43
+ * Like {@link zipC}, but keeps only the result from the `that` sink
44
+ *
45
+ * @tsplus fluent fncts.io.Sink apSecondC
46
+ */
47
+ export function apSecondC<R, E, In, L, Z, R1, E1, In1 extends In, L1 extends L, Z1>(
48
+ self: Sink<R, E, In, L, Z>,
49
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
50
+ __tsplusTrace?: string,
51
+ ): Sink<R & R1, E | E1, In & In1, L | L1, Z1> {
52
+ return self.zipWithC(that, (_, z1) => z1);
53
+ }
54
+
55
+ /**
56
+ * Replaces this sink's result with the provided value.
57
+ *
58
+ * @tsplus fluent fncts.io.Sink as
59
+ */
60
+ export function as<R, E, In, L, Z, Z1>(
61
+ self: Sink<R, E, In, L, Z>,
62
+ z: Lazy<Z1>,
63
+ __tsplusTrace?: string,
64
+ ): Sink<R, E, In, L, Z1> {
65
+ return self.map(() => z());
66
+ }
67
+
68
+ /**
69
+ * Repeatedly runs the sink and accumulates its results into a chunk
70
+ *
71
+ * @tsplus fluent fncts.io.Sink collectAll
72
+ */
73
+ export function collectAll<R, E, In extends L, L, Z>(
74
+ self: Sink<R, E, In, L, Z>,
75
+ __tsplusTrace?: string,
76
+ ): Sink<R, E, In, L, Conc<Z>> {
77
+ return self.collectAllWhileWith(
78
+ Conc.empty<Z>(),
79
+ () => true,
80
+ (s, z) => s.append(z),
6
81
  );
7
82
  }
8
83
 
9
84
  /**
10
- * A sink that collects all of its inputs into a chunk.
85
+ * Repeatedly runs the sink for as long as its results satisfy the predicate
86
+ * `p`. The sink's results will be accumulated using the stepping function
87
+ * `f`.
11
88
  *
12
- * @tsplus static fncts.io.SinkOps collectAll
89
+ * @tsplus fluent fncts.io.Sink collectAllWhileWith
90
+ */
91
+ export function collectAllWhileWith<R, E, In extends L, L, Z, S>(
92
+ self: Sink<R, E, In, L, Z>,
93
+ z: Lazy<S>,
94
+ p: Predicate<Z>,
95
+ f: (s: S, z: Z) => S,
96
+ __tsplusTrace?: string,
97
+ ): Sink<R, E, In, L, S> {
98
+ return new Sink(
99
+ Channel.fromIO(Ref.make<Conc<In>>(Conc.empty()).zip(Ref.make(false))).flatMap(([leftoversRef, upstreamDoneRef]) => {
100
+ const upstreamMarker: Channel<unknown, never, Conc<In>, unknown, never, Conc<In>, unknown> = Channel.readWith(
101
+ (inp) => Channel.writeNow(inp) > upstreamMarker,
102
+ Channel.failNow,
103
+ (x) => Channel.fromIO(upstreamDoneRef.set(true)).as(x),
104
+ );
105
+
106
+ function loop(currentResult: S): Channel<R, never, Conc<In>, unknown, E, Conc<L>, S> {
107
+ return self.channel.collectElements.matchChannel(Channel.failNow, ([leftovers, doneValue]) => {
108
+ if (p(doneValue)) {
109
+ return (
110
+ Channel.fromIO(leftoversRef.set(leftovers.flatten as Conc<In>)) >
111
+ Channel.fromIO(upstreamDoneRef.get).flatMap((upstreamDone) => {
112
+ const accumulatedResult = f(currentResult, doneValue);
113
+ if (upstreamDone) return Channel.writeNow(leftovers.flatten).as(accumulatedResult);
114
+ else return loop(accumulatedResult);
115
+ })
116
+ );
117
+ } else {
118
+ return Channel.writeNow(leftovers.flatten).as(currentResult);
119
+ }
120
+ });
121
+ }
122
+
123
+ return upstreamMarker.pipeTo(Channel.bufferChunk(leftoversRef)).pipeTo(loop(z()));
124
+ }),
125
+ );
126
+ }
127
+
128
+ /**
129
+ * Collects the leftovers from the stream when the sink succeeds and returns
130
+ * them as part of the sink's result
131
+ *
132
+ * @tsplus getter fncts.io.Sink collectLeftover
133
+ */
134
+ export function collectLeftover<R, E, In, L, Z>(
135
+ self: Sink<R, E, In, L, Z>,
136
+ __tsplusTrace?: string,
137
+ ): Sink<R, E, In, never, readonly [Z, Conc<L>]> {
138
+ return new Sink(self.channel.collectElements.map(([chunks, z]) => [z, chunks.flatten]));
139
+ }
140
+
141
+ /**
142
+ * Transforms this sink's input elements.
143
+ *
144
+ * @tsplus fluent fncts.io.Sink contramap
145
+ */
146
+ export function contramap<R, E, In, L, Z, In1>(
147
+ self: Sink<R, E, In, L, Z>,
148
+ f: (inp: In1) => In,
149
+ __tsplusTrace?: string,
150
+ ): Sink<R, E, In1, L, Z> {
151
+ return self.contramapChunks((chunk) => chunk.map(f));
152
+ }
153
+
154
+ /**
155
+ * Transforms this sink's input chunks. `f` must preserve chunking-invariance
156
+ *
157
+ * @tsplus fluent fncts.io.Sink contramapChunks
158
+ */
159
+ export function contramapChunks<R, E, In, L, Z, In1>(
160
+ self: Sink<R, E, In, L, Z>,
161
+ f: (chunk: Conc<In1>) => Conc<In>,
162
+ __tsplusTrace?: string,
163
+ ): Sink<R, E, In1, L, Z> {
164
+ const loop: Channel<R, never, Conc<In1>, unknown, never, Conc<In>, unknown> = Channel.readWith(
165
+ (chunk) => Channel.writeNow(f(chunk)) > loop,
166
+ Channel.failNow,
167
+ Channel.succeedNow,
168
+ );
169
+ return new Sink(loop >>> self.channel);
170
+ }
171
+
172
+ /**
173
+ * Effectfully transforms this sink's input chunks. `f` must preserve
174
+ * chunking-invariance
175
+ *
176
+ * @tsplus fluent fncts.io.Sink contramapChunksIO
177
+ */
178
+ export function contramapChunksIO<R, E, In, L, Z, R1, E1, In1>(
179
+ self: Sink<R, E, In, L, Z>,
180
+ f: (chunk: Conc<In1>) => IO<R1, E1, Conc<In>>,
181
+ __tsplusTrace?: string,
182
+ ): Sink<R & R1, E | E1, In1, L, Z> {
183
+ const loop: Channel<R & R1, never, Conc<In1>, unknown, E | E1, Conc<In>, unknown> = Channel.readWith(
184
+ (chunk) => Channel.fromIO(f(chunk)).flatMap(Channel.writeNow) > loop,
185
+ Channel.failNow,
186
+ Channel.succeedNow,
187
+ );
188
+ return new Sink(loop.pipeToOrFail(self.channel));
189
+ }
190
+
191
+ /**
192
+ * Effectfully transforms this sink's input elements.
193
+ *
194
+ * @tsplus fluent fncts.io.Sink contramapIO
195
+ */
196
+ export function contramapIO<R, E, In, L, Z, R1, E1, In1>(
197
+ self: Sink<R, E, In, L, Z>,
198
+ f: (inp: In1) => IO<R1, E1, In>,
199
+ __tsplusTrace?: string,
200
+ ): Sink<R & R1, E | E1, In1, L, Z> {
201
+ return self.contramapChunksIO((chunk) => chunk.mapIO(f));
202
+ }
203
+
204
+ /**
205
+ * Transforms both inputs and result of this sink using the provided
206
+ * functions.
207
+ *
208
+ * @tsplus fluent fncts.io.Sink dimap
209
+ */
210
+ export function dimap<R, E, In, L, Z, In1, Z1>(
211
+ self: Sink<R, E, In, L, Z>,
212
+ f: (inp: In1) => In,
213
+ g: (z: Z) => Z1,
214
+ __tsplusTrace?: string,
215
+ ): Sink<R, E, In1, L, Z1> {
216
+ return self.contramap(f).map(g);
217
+ }
218
+
219
+ /**
220
+ * Transforms both input chunks and result of this sink using the provided
221
+ * functions.
222
+ *
223
+ * @tsplus fluent fncts.io.Sink dimapChunks
224
+ */
225
+ export function dimapChunks<R, E, In, L, Z, In1, Z1>(
226
+ self: Sink<R, E, In, L, Z>,
227
+ f: (chunk: Conc<In1>) => Conc<In>,
228
+ g: (z: Z) => Z1,
229
+ __tsplusTrace?: string,
230
+ ): Sink<R, E, In1, L, Z1> {
231
+ return self.contramapChunks(f).map(g);
232
+ }
233
+
234
+ /**
235
+ * Effectfully transforms both input chunks and result of this sink using the
236
+ * provided functions. `f` and `g` must preserve chunking-invariance
237
+ *
238
+ * @tsplus fluent fncts.io.Sink dimapChunksIO
239
+ */
240
+ export function dimapChunksIO<R, E, In, L, Z, R1, E1, In1, R2, E2, Z1>(
241
+ self: Sink<R, E, In, L, Z>,
242
+ f: (chunk: Conc<In1>) => IO<R1, E1, Conc<In>>,
243
+ g: (z: Z) => IO<R2, E2, Z1>,
244
+ __tsplusTrace?: string,
245
+ ): Sink<R & R1 & R2, E | E1 | E2, In1, L, Z1> {
246
+ return self.contramapChunksIO(f).mapIO(g);
247
+ }
248
+
249
+ /**
250
+ * Effectfully transforms both inputs and result of this sink using the
251
+ * provided functions.
252
+ *
253
+ * @tsplus fluent fncts.io.Sink dimapIO
254
+ */
255
+ export function dimapIO<R, E, In, L, Z, R1, E1, In1, R2, E2, Z1>(
256
+ self: Sink<R, E, In, L, Z>,
257
+ f: (inp: In1) => IO<R1, E1, In>,
258
+ g: (z: Z) => IO<R2, E2, Z1>,
259
+ __tsplusTrace?: string,
260
+ ): Sink<R & R1 & R2, E | E1 | E2, In1, L, Z1> {
261
+ return self.contramapIO(f).mapIO(g);
262
+ }
263
+
264
+ /**
265
+ * Returns a lazily constructed sink that may require effects for its
266
+ * creation.
267
+ *
268
+ * @tsplus static fncts.io.SinkOps defer
13
269
  */
14
- export function collectAll<Err, A>(): Sink<unknown, Err, A, never, Conc<A>> {
15
- return new Sink(collectLoop<Err, A>(Conc.empty()));
270
+ export function defer<R, E, In, L, Z>(sink: Lazy<Sink<R, E, In, L, Z>>, __tsplusTrace?: string): Sink<R, E, In, L, Z> {
271
+ return new Sink(Channel.defer(sink().channel));
16
272
  }
17
273
 
18
274
  const drainLoop: Channel<unknown, never, Conc<unknown>, unknown, never, Conc<never>, void> = Channel.readWithCause(
@@ -29,9 +285,58 @@ const drainLoop: Channel<unknown, never, Conc<unknown>, unknown, never, Conc<nev
29
285
  export const drain: Sink<unknown, never, unknown, never, void> = new Sink(drainLoop);
30
286
 
31
287
  /**
288
+ * Drops incoming elements until the predicate `p` is satisfied.
289
+ *
290
+ * @tsplus static fncts.io.SinkOps dropUntil
291
+ */
292
+ export function makeDropUntil<In>(p: Predicate<In>, __tsplusTrace?: string): Sink<unknown, never, In, In, void> {
293
+ const loop: Channel<unknown, never, Conc<In>, any, never, Conc<In>, void> = Channel.readWith(
294
+ (inp: Conc<In>) => {
295
+ const leftover = inp.dropUntil(p);
296
+ const more = leftover.isEmpty;
297
+ if (more) {
298
+ return loop;
299
+ } else {
300
+ return Channel.writeNow(leftover) > Channel.id<never, Conc<In>, void>();
301
+ }
302
+ },
303
+ Channel.failNow,
304
+ () => Channel.unit,
305
+ );
306
+ return new Sink(loop);
307
+ }
308
+
309
+ /**
310
+ * Drops incoming elements until the effectful predicate `p` is satisfied.
311
+ *
312
+ * @tsplus static fncts.io.SinkOps dropUntilIO
313
+ */
314
+ export function makeDropUntilIO<R, E, In>(
315
+ p: (inp: In) => IO<R, E, boolean>,
316
+ __tsplusTrace?: string,
317
+ ): Sink<R, E, In, In, void> {
318
+ const loop: Channel<R, E, Conc<In>, any, E, Conc<In>, void> = Channel.readWith(
319
+ (inp: Conc<In>) =>
320
+ Channel.unwrap(
321
+ inp
322
+ .dropUntilIO(p)
323
+ .map((leftover) => (leftover.isEmpty ? loop : Channel.writeNow(leftover) > Channel.id<E, Conc<In>, void>())),
324
+ ),
325
+ Channel.failNow,
326
+ () => Channel.unit,
327
+ );
328
+ return new Sink(loop);
329
+ }
330
+
331
+ /**
332
+ * Drops incoming elements as long as the predicate `p` is satisfied.
333
+ *
32
334
  * @tsplus static fncts.io.SinkOps dropWhile
33
335
  */
34
- export function dropWhile<Err, In>(predicate: Predicate<In>): Sink<unknown, never, In, In, any> {
336
+ export function makeDropWhile<Err, In>(
337
+ predicate: Predicate<In>,
338
+ __tsplusTrace?: string,
339
+ ): Sink<unknown, never, In, In, any> {
35
340
  const loop: Channel<unknown, never, Conc<In>, any, never, Conc<In>, any> = Channel.readWith(
36
341
  (inp: Conc<In>) => {
37
342
  const leftover = inp.dropWhile(predicate);
@@ -49,40 +354,1424 @@ export function dropWhile<Err, In>(predicate: Predicate<In>): Sink<unknown, neve
49
354
  }
50
355
 
51
356
  /**
52
- * A sink that executes the provided effectful function for every element fed to it.
357
+ * Drops incoming elements as long as the effectful predicate `p` is
358
+ * satisfied.
53
359
  *
54
- * @tsplus static fncts.io.SinkOps foreach
360
+ * @tsplus static fncts.io.SinkOps dropWhileIO
55
361
  */
56
- export function foreach<R, Err, In>(f: (inp: In) => IO<R, Err, any>): Sink<R, Err, In, In, void> {
57
- return Sink.foreachWhile((inp) => f(inp).as(true));
362
+ export function dropWhileIO<R, E, In>(
363
+ p: (inp: In) => IO<R, E, boolean>,
364
+ __tsplusTrace?: string,
365
+ ): Sink<R, E, In, In, void> {
366
+ const loop: Channel<R, E, Conc<In>, any, E, Conc<In>, void> = Channel.readWith(
367
+ (inp: Conc<In>) =>
368
+ Channel.unwrap(
369
+ inp
370
+ .dropWhileIO(p)
371
+ .map((leftover) => (leftover.isEmpty ? loop : Channel.writeNow(leftover) > Channel.id<E, Conc<In>, void>())),
372
+ ),
373
+ Channel.failNow,
374
+ () => Channel.unit,
375
+ );
376
+ return new Sink(loop);
58
377
  }
59
378
 
60
- function foreachWhileLoop<R, Err, In>(
61
- f: (_: In) => IO<R, Err, boolean>,
62
- chunk: Conc<In>,
63
- idx: number,
64
- len: number,
65
- cont: Channel<R, Err, Conc<In>, unknown, Err, Conc<In>, void>,
66
- ): Channel<R, Err, Conc<In>, unknown, Err, Conc<In>, void> {
67
- if (idx === len) {
68
- return cont;
69
- }
70
- return Channel.fromIO(f(chunk.unsafeGet(idx)))
71
- .flatMap((b) => (b ? foreachWhileLoop(f, chunk, idx + 1, len, cont) : Channel.writeNow(chunk.drop(idx))))
72
- .catchAll((e) => Channel.writeNow(chunk.drop(idx)).apSecond(Channel.failNow(e)));
379
+ /**
380
+ * Accesses the whole environment of the sink.
381
+ *
382
+ * @tsplus static fncts.io.SinkOps environment
383
+ */
384
+ export function environment<R>(__tsplusTrace?: string): Sink<R, never, unknown, never, Environment<R>> {
385
+ return Sink.fromIO(IO.environment<R>());
73
386
  }
74
387
 
75
388
  /**
76
- * A sink that executes the provided effectful function for every element fed to it
77
- * until `f` evaluates to `false`.
389
+ * Accesses the environment of the sink.
78
390
  *
79
- * @tsplus static fncts.io.SinkOps foreachWhile
391
+ * @tsplus static fncts.io.SinkOps environmentWith
80
392
  */
81
- export function foreachWhile<R, Err, In>(f: (_: In) => IO<R, Err, boolean>): Sink<R, Err, In, In, void> {
82
- const process: Channel<R, Err, Conc<In>, unknown, Err, Conc<In>, void> = Channel.readWithCause(
83
- (inp: Conc<In>) => foreachWhileLoop(f, inp, 0, inp.length, process),
84
- Channel.failCauseNow,
85
- () => Channel.unit,
393
+ export function environmentWith<R, Z>(
394
+ f: (r: Environment<R>) => Z,
395
+ __tsplusTrace?: string,
396
+ ): Sink<R, never, unknown, never, Z> {
397
+ return Sink.fromIO(IO.environmentWith(f));
398
+ }
399
+
400
+ /**
401
+ * Accesses the environment of the sink in the context of an effect.
402
+ *
403
+ * @tsplus static fncts.io.SinkOps environmentWithIO
404
+ */
405
+ export function environmentWithIO<R, R1, E, Z>(
406
+ f: (r: Environment<R>) => IO<R1, E, Z>,
407
+ __tsplusTrace?: string,
408
+ ): Sink<R & R1, E, unknown, never, Z> {
409
+ return Sink.fromIO(IO.environmentWithIO(f));
410
+ }
411
+
412
+ /**
413
+ * Accesses the environment of the sink in the context of a sink.
414
+ *
415
+ * @tsplus static fncts.io.SinkOps environmentWithSink
416
+ */
417
+ export function environmentWithSink<R, R1, E, In, L, Z>(
418
+ f: (r: Environment<R>) => Sink<R1, E, In, L, Z>,
419
+ __tsplusTrace?: string,
420
+ ): Sink<R & R1, E, In, L, Z> {
421
+ return new Sink(Channel.unwrap(IO.environmentWith(f.compose((s) => s.channel))));
422
+ }
423
+
424
+ /**
425
+ * A sink that always fails with the specified error.
426
+ *
427
+ * @tsplus static fncts.io.SinkOps fail
428
+ */
429
+ export function fail<E>(e: Lazy<E>, __tsplusTrace?: string): Sink<unknown, E, unknown, never, never> {
430
+ return new Sink(Channel.fail(e));
431
+ }
432
+
433
+ /**
434
+ * Creates a sink halting with a specified cause.
435
+ *
436
+ * @tsplus static fncts.io.SinkOps failCause
437
+ */
438
+ export function failCause<E>(cause: Lazy<Cause<E>>, __tsplusTrace?: string): Sink<unknown, E, unknown, never, never> {
439
+ return new Sink(Channel.failCause(cause));
440
+ }
441
+
442
+ /**
443
+ * Creates a sink halting with a specified cause.
444
+ *
445
+ * @tsplus static fncts.io.SinkOps failCauseNow
446
+ */
447
+ export function failCauseNow<E>(cause: Cause<E>, __tsplusTrace?: string): Sink<unknown, E, unknown, never, never> {
448
+ return new Sink(Channel.failCauseNow(cause));
449
+ }
450
+
451
+ /**
452
+ * A sink that always fails with the specified error.
453
+ *
454
+ * @tsplus static fncts.io.SinkOps failNow
455
+ */
456
+ export function failNow<E>(e: E, __tsplusTrace?: string): Sink<unknown, E, unknown, never, never> {
457
+ return new Sink(Channel.failNow(e));
458
+ }
459
+
460
+ /**
461
+ * Filters the sink's input with the given predicate
462
+ *
463
+ * @tsplus static fncts.io.SinkOps filterInput
464
+ */
465
+ export function filterInput<R, E, In, L, Z>(
466
+ self: Sink<R, E, In, L, Z>,
467
+ p: Predicate<In>,
468
+ __tsplusTrace?: string,
469
+ ): Sink<R, E, In, L, Z> {
470
+ return self.contramapChunks((chunk) => chunk.filter(p));
471
+ }
472
+
473
+ /**
474
+ * Filters the sink's input with the given IO predicate
475
+ *
476
+ * @tsplus static fncts.io.SinkOps filterInputIO
477
+ */
478
+ export function filterInputIO<R, E, In, L, Z, R1, E1>(
479
+ self: Sink<R, E, In, L, Z>,
480
+ p: (inp: In) => IO<R1, E1, boolean>,
481
+ __tsplusTrace?: string,
482
+ ): Sink<R & R1, E | E1, In, L, Z> {
483
+ return self.contramapChunksIO((chunk) => chunk.filterIO(p));
484
+ }
485
+
486
+ /**
487
+ * Creates a sink that produces values until one verifies the predicate `f`.
488
+ *
489
+ * @tsplus fluent fncts.io.Sink findIO
490
+ */
491
+ export function findIO<R, E, In extends L, L, Z, R1, E1>(
492
+ self: Sink<R, E, In, L, Z>,
493
+ f: (z: Z) => IO<R1, E1, boolean>,
494
+ __tsplusTrace?: string,
495
+ ): Sink<R & R1, E | E1, In, L, Maybe<Z>> {
496
+ return new Sink(
497
+ Channel.fromIO(Ref.make(Conc.empty<In>()).zip(Ref.make(false))).flatMap(([leftoversRef, upstreamDoneRef]) => {
498
+ const upstreamMarker: Channel<unknown, never, Conc<In>, unknown, never, Conc<In>, unknown> = Channel.readWith(
499
+ (inp) => Channel.writeNow(inp) > upstreamMarker,
500
+ Channel.failNow,
501
+ (x) => Channel.fromIO(upstreamDoneRef.set(true)).as(x),
502
+ );
503
+
504
+ const loop: Channel<
505
+ R & R1,
506
+ never,
507
+ Conc<In>,
508
+ unknown,
509
+ E | E1,
510
+ Conc<L>,
511
+ Maybe<Z>
512
+ > = self.channel.collectElements.matchChannel(Channel.failNow, ([leftovers, doneValue]) =>
513
+ Channel.fromIO(f(doneValue)).flatMap(
514
+ (satisfied) =>
515
+ Channel.fromIO(leftoversRef.set(leftovers.flatten as Conc<In>)) >
516
+ Channel.fromIO(upstreamDoneRef.get).flatMap((upstreamDone) => {
517
+ if (satisfied) return Channel.writeNow(leftovers.flatten).as(Just(doneValue));
518
+ else if (upstreamDone) return Channel.writeNow(leftovers.flatten).as(Nothing());
519
+ else return loop;
520
+ }),
521
+ ),
522
+ );
523
+
524
+ return (upstreamMarker >>> Channel.bufferChunk(leftoversRef)) >>> loop;
525
+ }),
526
+ );
527
+ }
528
+
529
+ /**
530
+ * Runs this sink until it yields a result, then uses that result to create
531
+ * another sink from the provided function which will continue to run until it
532
+ * yields a result.
533
+ *
534
+ * This function essentially runs sinks in sequence.
535
+ *
536
+ * @tsplus fluent fncts.io.Sink flatMap
537
+ */
538
+ export function flatMap<R, E, In, L, Z, R1, E1, In1 extends In, L1, Z1>(
539
+ self: Sink<R, E, In, L, Z>,
540
+ f: (z: Z) => Sink<R1, E1, In1, L1, Z1>,
541
+ __tsplusTrace?: string,
542
+ ): Sink<R & R1, E | E1, In1, L | L1, Z1> {
543
+ return self.matchSink(Sink.failNow, f);
544
+ }
545
+
546
+ /**
547
+ * Creates a sink from a {@link Channel}
548
+ *
549
+ * @tsplus static fncts.io.SinkOps fromChannel
550
+ * @tsplus static fncts.io.SinkOps __call
551
+ */
552
+ export function fromChannel<R, E, In, L, Z>(
553
+ channel: Channel<R, never, Conc<In>, unknown, E, Conc<L>, Z>,
554
+ ): Sink<R, E, In, L, Z> {
555
+ return new Sink(channel);
556
+ }
557
+
558
+ /**
559
+ * Creates a sink from a chunk processing function.
560
+ *
561
+ * @tsplus static fncts.io.SinkOps fromPush
562
+ */
563
+ export function fromPush<R, E, In, L, Z, R1>(
564
+ push: IO<Has<Scope> & R, never, (_: Maybe<Conc<In>>) => IO<R1, readonly [Either<E, Z>, Conc<L>], void>>,
565
+ __tsplusTrace?: string,
566
+ ): Sink<R & R1, E, In, L, Z> {
567
+ return new Sink(Channel.unwrapScoped(push.map(fromPushPull)));
568
+ }
569
+
570
+ function fromPushPull<R, E, In, L, Z>(
571
+ push: (_: Maybe<Conc<In>>) => IO<R, readonly [Either<E, Z>, Conc<L>], void>,
572
+ ): Channel<R, never, Conc<In>, unknown, E, Conc<L>, Z> {
573
+ return Channel.readWith(
574
+ (inp: Conc<In>) =>
575
+ Channel.fromIO(push(Just(inp))).matchChannel(
576
+ ([r, leftovers]) =>
577
+ r.match(
578
+ (e) => Channel.writeNow(leftovers) > Channel.failNow(e),
579
+ (z) => Channel.writeNow(leftovers) > Channel.succeedNow(z),
580
+ ),
581
+ () => fromPushPull(push),
582
+ ),
583
+ Channel.failNow,
584
+ () =>
585
+ Channel.fromIO(push(Nothing())).matchChannel(
586
+ ([r, leftovers]) =>
587
+ r.match(
588
+ (e) => Channel.writeNow(leftovers) > Channel.failNow(e),
589
+ (z) => Channel.writeNow(leftovers) > Channel.succeedNow(z),
590
+ ),
591
+ () => Channel.fromIO(IO.halt(new Error("empty sink"))),
592
+ ),
593
+ );
594
+ }
595
+
596
+ /**
597
+ * Create a sink which enqueues each element into the specified queue.
598
+ *
599
+ * @tsplus static fncts.io.SinkOps fromQueue
600
+ */
601
+ export function fromQueue<In>(
602
+ queue: Lazy<Queue.Enqueue<In>>,
603
+ __tsplusTrace?: string,
604
+ ): Sink<unknown, never, In, never, void> {
605
+ return Sink.unwrap(IO.succeed(queue).map((queue) => Sink.foreachChunk((inp) => queue.offerAll(inp))));
606
+ }
607
+
608
+ /**
609
+ * Create a sink which enqueues each element into the specified queue. The
610
+ * queue will be shutdown once the stream is closed.
611
+ *
612
+ * @tsplus static fncts.io.SinkOps fromQueueWithShutdown
613
+ */
614
+ export function fromQueueWithShutdown<In>(
615
+ queue: Lazy<Queue.Enqueue<In>>,
616
+ __tsplusTrace?: string,
617
+ ): Sink<unknown, never, In, never, void> {
618
+ return Sink.unwrapScoped(
619
+ IO.succeed(queue)
620
+ .acquireRelease((queue) => queue.shutdown)
621
+ .map((queue) => Sink.fromQueue(queue)),
622
+ );
623
+ }
624
+
625
+ /**
626
+ * Create a sink which publishes each element to the specified hub.
627
+ *
628
+ * @tsplus static fncts.io.SinkOps fromHub
629
+ */
630
+ export function fromHub<In>(hub: Lazy<Hub<In>>, __tsplusTrace?: string): Sink<unknown, never, In, never, void> {
631
+ return Sink.fromQueue(hub);
632
+ }
633
+
634
+ /**
635
+ * Create a sink which publishes each element to the specified hub. The hub
636
+ * will be shutdown once the stream is closed.
637
+ *
638
+ * @tsplus static fncts.io.SinkOps fromHubWithShutdown
639
+ */
640
+ export function fromHubWithShutdown<In>(
641
+ hub: Lazy<Hub<In>>,
642
+ __tsplusTrace?: string,
643
+ ): Sink<unknown, never, In, never, void> {
644
+ return Sink.fromQueueWithShutdown(hub);
645
+ }
646
+
647
+ /**
648
+ * Creates a single-value sink produced from an effect
649
+ *
650
+ * @tsplus static fncts.io.SinkOps fromIO
651
+ */
652
+ export function fromIO<R, E, Z>(b: Lazy<IO<R, E, Z>>, __tsplusTrace?: string): Sink<R, E, unknown, never, Z> {
653
+ return new Sink(Channel.fromIO(b));
654
+ }
655
+
656
+ /**
657
+ * Creates a sink halting with the specified unchecked value.
658
+ *
659
+ * @tsplus static fncts.io.SinkOps halt
660
+ */
661
+ export function halt(defect: Lazy<unknown>, __tsplusTrace?: string): Sink<unknown, never, unknown, never, never> {
662
+ return Sink.failCause(Cause.halt(defect()));
663
+ }
664
+
665
+ /**
666
+ * Creates a sink halting with the specified unchecked value.
667
+ *
668
+ * @tsplus static fncts.io.SinkOps haltNow
669
+ */
670
+ export function haltNow(defect: unknown, __tsplusTrace?: string): Sink<unknown, never, unknown, never, never> {
671
+ return Sink.failCauseNow(Cause.halt(defect));
672
+ }
673
+
674
+ /**
675
+ * Creates a sink containing the first value.
676
+ *
677
+ * @tsplus static fncts.io.SinkOps head
678
+ */
679
+ export function head<In>(__tsplusTrace?: string): Sink<unknown, never, In, In, Maybe<In>> {
680
+ return Sink.fold(
681
+ Nothing(),
682
+ (elem) => elem.isNothing(),
683
+ (s, inp) =>
684
+ s.match(
685
+ () => Just(inp),
686
+ () => s,
687
+ ),
688
+ );
689
+ }
690
+
691
+ /**
692
+ * Drains the remaining elements from the stream after the sink finishes
693
+ *
694
+ * @tsplus getter fncts.io.Sink ignoreLeftover
695
+ */
696
+ export function ignoreLeftover<R, E, In, L, Z>(
697
+ self: Sink<R, E, In, L, Z>,
698
+ __tsplusTrace?: string,
699
+ ): Sink<R, E, In, never, Z> {
700
+ return new Sink(self.channel.drain);
701
+ }
702
+
703
+ /**
704
+ * Creates a sink containing the last value.
705
+ *
706
+ * @tsplus static fncts.io.SinkOps last
707
+ */
708
+ export function last<In>(__tsplusTrace?: string): Sink<unknown, never, In, In, Maybe<In>> {
709
+ return Sink.foldLeft(Nothing(), (_, inp) => Just(inp));
710
+ }
711
+
712
+ /**
713
+ * Creates a sink that does not consume any input but provides the given chunk
714
+ * as its leftovers
715
+ *
716
+ * @tsplus static fncts.io.SinkOps leftover
717
+ */
718
+ export function leftover<L>(c: Lazy<Conc<L>>, __tsplusTrace?: string): Sink<unknown, never, unknown, L, void> {
719
+ return new Sink(Channel.write(c));
720
+ }
721
+
722
+ /**
723
+ * Logs the specified message at the current log level.
724
+ *
725
+ * @tsplus static fncts.io.SinkOps log
726
+ */
727
+ export function log(message: Lazy<string>, __tsplusTrace?: string): Sink<unknown, never, unknown, never, void> {
728
+ return Sink.fromIO(IO.log(message));
729
+ }
730
+
731
+ /**
732
+ * A sink that collects all of its inputs into a chunk.
733
+ *
734
+ * @tsplus static fncts.io.SinkOps collectAll
735
+ */
736
+ export function makeCollectAll<In>(): Sink<unknown, never, In, never, Conc<In>> {
737
+ return new Sink(collectLoop<In>(Conc.empty()));
738
+ }
739
+
740
+ function collectLoop<A>(state: Conc<A>): Channel<unknown, never, Conc<A>, unknown, never, Conc<never>, Conc<A>> {
741
+ return Channel.readWithCause(
742
+ (inp: Conc<A>) => collectLoop(state.concat(inp)),
743
+ Channel.failCauseNow,
744
+ () => Channel.endNow(state),
745
+ );
746
+ }
747
+
748
+ /**
749
+ * A sink that collects first `n` elements into a chunk. Note that the chunk
750
+ * is preallocated and must fit in memory.
751
+ *
752
+ * @tsplus static fncts.io.SinkOps collectAllN
753
+ */
754
+ export function makeCollectAllN<In>(n: Lazy<number>): Sink<unknown, never, In, In, Conc<In>> {
755
+ return Sink.fromIO(IO.succeed(new ConcBuilder<In>())).flatMap((builder) =>
756
+ Sink.foldUntil<In, ConcBuilder<In>>(builder, n, (builder, inp) => builder.append(inp)).map((builder) =>
757
+ builder.result(),
758
+ ),
759
+ );
760
+ }
761
+
762
+ /**
763
+ * A sink that executes the provided effectful function for every element fed to it.
764
+ *
765
+ * @tsplus static fncts.io.SinkOps foreach
766
+ */
767
+ export function makeForeach<R, Err, In>(
768
+ f: (inp: In) => IO<R, Err, any>,
769
+ __tsplusTrace?: string,
770
+ ): Sink<R, Err, In, In, void> {
771
+ return Sink.foreachWhile((inp) => f(inp).as(true));
772
+ }
773
+
774
+ /**
775
+ * A sink that executes the provided effectful function for every chunk fed to
776
+ * it.
777
+ *
778
+ * @tsplus static fncts.io.SinkOps foreachChunk
779
+ */
780
+ export function makeForeachChunk<R, E, In>(
781
+ f: (inp: Conc<In>) => IO<R, E, void>,
782
+ __tsplusTrace?: string,
783
+ ): Sink<R, E, In, never, void> {
784
+ const process: Channel<R, E, Conc<In>, unknown, E, never, void> = Channel.readWithCause(
785
+ (inp: Conc<In>) => Channel.fromIO(f(inp)) > process,
786
+ Channel.failCauseNow,
787
+ () => Channel.unit,
788
+ );
789
+
790
+ return new Sink(process);
791
+ }
792
+
793
+ /**
794
+ * A sink that executes the provided effectful function for every element fed to it
795
+ * until `f` evaluates to `false`.
796
+ *
797
+ * @tsplus static fncts.io.SinkOps foreachWhile
798
+ */
799
+ export function makeForeachWhile<R, Err, In>(
800
+ f: (_: In) => IO<R, Err, boolean>,
801
+ __tsplusTrace?: string,
802
+ ): Sink<R, Err, In, In, void> {
803
+ const process: Channel<R, Err, Conc<In>, unknown, Err, Conc<In>, void> = Channel.readWithCause(
804
+ (inp: Conc<In>) => foreachWhileLoop(f, inp, 0, inp.length, process),
805
+ Channel.failCauseNow,
806
+ () => Channel.unit,
807
+ );
808
+ return new Sink(process);
809
+ }
810
+
811
+ function foreachWhileLoop<R, Err, In>(
812
+ f: (_: In) => IO<R, Err, boolean>,
813
+ chunk: Conc<In>,
814
+ idx: number,
815
+ len: number,
816
+ cont: Channel<R, Err, Conc<In>, unknown, Err, Conc<In>, void>,
817
+ ): Channel<R, Err, Conc<In>, unknown, Err, Conc<In>, void> {
818
+ if (idx === len) {
819
+ return cont;
820
+ }
821
+ return Channel.fromIO(f(chunk.unsafeGet(idx)))
822
+ .flatMap((b) => (b ? foreachWhileLoop(f, chunk, idx + 1, len, cont) : Channel.writeNow(chunk.drop(idx))))
823
+ .catchAll((e) => Channel.writeNow(chunk.drop(idx)).apSecond(Channel.failNow(e)));
824
+ }
825
+
826
+ /**
827
+ * A sink that executes the provided effectful function for every chunk fed to
828
+ * it until `f` evaluates to `false`.
829
+ *
830
+ * @tsplus static fncts.io.SinkOps foreachChunkWhile
831
+ */
832
+ export function makeForeachChunkWhile<R, E, In>(
833
+ f: (chunk: Conc<In>) => IO<R, E, boolean>,
834
+ __tsplusTrace?: string,
835
+ ): Sink<R, E, In, In, void> {
836
+ const reader: Channel<R, E, Conc<In>, unknown, E, never, void> = Channel.readWith(
837
+ (inp: Conc<In>) => Channel.fromIO(f(inp)).flatMap((cont) => (cont ? reader : Channel.unit)),
838
+ Channel.failNow,
839
+ () => Channel.unit,
840
+ );
841
+
842
+ return new Sink(reader);
843
+ }
844
+
845
+ /**
846
+ * A sink that folds its inputs with the provided function, termination
847
+ * predicate and initial state.
848
+ *
849
+ * @tsplus static fncts.io.SinkOps fold
850
+ */
851
+ export function makeFold<In, S>(
852
+ z: Lazy<S>,
853
+ contFn: Predicate<S>,
854
+ f: (s: S, inp: In) => S,
855
+ __tsplusTrace?: string,
856
+ ): Sink<unknown, never, In, In, S> {
857
+ return Sink.defer(new Sink(foldReader(z(), contFn, f)));
858
+ }
859
+
860
+ /**
861
+ * @tsplus tailRec
862
+ */
863
+ function foldChunkSplit<S, In>(
864
+ contFn: (s: S) => boolean,
865
+ f: (s: S, inp: In) => S,
866
+ s: S,
867
+ chunk: Conc<In>,
868
+ idx: number,
869
+ len: number,
870
+ ): readonly [S, Conc<In>] {
871
+ if (idx === len) {
872
+ return [s, Conc.empty()];
873
+ } else {
874
+ const s1 = f(s, chunk[idx]);
875
+ if (contFn(s1)) {
876
+ return foldChunkSplit(contFn, f, s1, chunk, idx + 1, len);
877
+ } else {
878
+ return [s1, chunk.drop(idx + 1)];
879
+ }
880
+ }
881
+ }
882
+
883
+ function foldReader<S, In>(
884
+ s: S,
885
+ contFn: Predicate<S>,
886
+ f: (s: S, inp: In) => S,
887
+ ): Channel<unknown, never, Conc<In>, any, never, Conc<In>, S> {
888
+ if (!contFn(s)) {
889
+ return Channel.succeedNow(s);
890
+ } else {
891
+ return Channel.readWith(
892
+ (inp: Conc<In>) => {
893
+ const [nextS, leftovers] = foldChunkSplit(contFn, f, s, inp, 0, inp.length);
894
+ if (leftovers.isNonEmpty) {
895
+ return Channel.writeNow(leftovers).as(nextS);
896
+ } else {
897
+ return foldReader(nextS, contFn, f);
898
+ }
899
+ },
900
+ (_: never) => Channel.failNow(_),
901
+ (_: S) => Channel.succeedNow(_),
902
+ );
903
+ }
904
+ }
905
+
906
+ /**
907
+ * Creates a sink that folds elements of type `In` into a structure of type
908
+ * `S` until `max` elements have been folded.
909
+ *
910
+ * Like {@link foldWeighted}, but with a constant cost function of 1.
911
+ *
912
+ * @tsplus static fncts.io.SinkOps foldUntil
913
+ */
914
+ export function makeFoldUntil<In, S>(
915
+ z: Lazy<S>,
916
+ max: Lazy<number>,
917
+ f: (s: S, inp: In) => S,
918
+ __tsplusTrace?: string,
919
+ ): Sink<unknown, never, In, In, S> {
920
+ return Sink.unwrap(
921
+ IO.succeed(max).map((max) =>
922
+ Sink.fold<In, readonly [S, number]>(
923
+ [z(), 0],
924
+ ([_, n]) => n < max,
925
+ ([o, count], i) => [f(o, i), count + 1],
926
+ ).map(([s]) => s),
927
+ ),
928
+ );
929
+ }
930
+
931
+ /**
932
+ * A sink that folds its input chunks with the provided function, termination
933
+ * predicate and initial state. `contFn` condition is checked only for the
934
+ * initial value and at the end of processing of each chunk. `f` and `contFn`
935
+ * must preserve chunking-invariance.
936
+ *
937
+ * @tsplus static fncts.io.SinkOps foldChunks
938
+ */
939
+ export function makeFoldChunks<In, S>(
940
+ z: Lazy<S>,
941
+ contFn: Predicate<S>,
942
+ f: (s: S, inp: Conc<In>) => S,
943
+ __tsplusTrace?: string,
944
+ ): Sink<unknown, never, In, never, S> {
945
+ return Sink.defer(new Sink(foldChunksReader(z(), contFn, f)));
946
+ }
947
+
948
+ function foldChunksReader<In, S>(
949
+ s: S,
950
+ contFn: Predicate<S>,
951
+ f: (s: S, inp: Conc<In>) => S,
952
+ ): Channel<unknown, never, Conc<In>, unknown, never, never, S> {
953
+ if (!contFn(s)) {
954
+ return Channel.succeedNow(s);
955
+ } else {
956
+ return Channel.readWith(
957
+ (inp: Conc<In>) => {
958
+ const nextS = f(s, inp);
959
+ return foldChunksReader(nextS, contFn, f);
960
+ },
961
+ (err: never) => Channel.failNow(err),
962
+ (_: any) => Channel.succeedNow(_),
963
+ );
964
+ }
965
+ }
966
+
967
+ /**
968
+ * A sink that effectfully folds its input chunks with the provided function,
969
+ * termination predicate and initial state. `contFn` condition is checked only
970
+ * for the initial value and at the end of processing of each chunk. `f` and
971
+ * `contFn` must preserve chunking-invariance.
972
+ *
973
+ * @tsplus static fncts.io.SinkOps foldChunksIO
974
+ */
975
+ export function makeFoldChunksIO<Env, Err, In, S>(
976
+ z: Lazy<S>,
977
+ contFn: Predicate<S>,
978
+ f: (s: S, inp: Conc<In>) => IO<Env, Err, S>,
979
+ __tsplusTrace?: string,
980
+ ): Sink<Env, Err, In, In, S> {
981
+ return Sink.defer(new Sink(foldChunksIOReader(z(), contFn, f)));
982
+ }
983
+
984
+ function foldChunksIOReader<Env, Err, In, S>(
985
+ s: S,
986
+ contFn: Predicate<S>,
987
+ f: (s: S, inp: Conc<In>) => IO<Env, Err, S>,
988
+ ): Channel<Env, Err, Conc<In>, unknown, Err, never, S> {
989
+ if (!contFn(s)) {
990
+ return Channel.succeedNow(s);
991
+ } else {
992
+ return Channel.readWith(
993
+ (inp: Conc<In>) => Channel.fromIO(f(s, inp)).flatMap((s) => foldChunksIOReader(s, contFn, f)),
994
+ (err: Err) => Channel.failNow(err),
995
+ (_: any) => Channel.succeedNow(_),
996
+ );
997
+ }
998
+ }
999
+
1000
+ /**
1001
+ * A sink that folds its inputs with the provided function and initial state.
1002
+ *
1003
+ * @tsplus static fncts.io.SinkOps foldLeft
1004
+ */
1005
+ export function makeFoldLeft<In, S>(z: Lazy<S>, f: (s: S, inp: In) => S): Sink<unknown, never, In, never, S> {
1006
+ return Sink.fold(z, () => true, f).ignoreLeftover;
1007
+ }
1008
+
1009
+ /**
1010
+ * A sink that folds its input chunks with the provided function and initial
1011
+ * state. `f` must preserve chunking-invariance.
1012
+ *
1013
+ * @tsplus static fncts.io.SinkOps foldLeftChunks
1014
+ */
1015
+ export function makeFoldLeftChunks<In, S>(
1016
+ z: Lazy<S>,
1017
+ f: (s: S, inp: Conc<In>) => S,
1018
+ ): Sink<unknown, never, In, never, S> {
1019
+ return Sink.foldChunks(z, () => true, f).ignoreLeftover;
1020
+ }
1021
+
1022
+ /**
1023
+ * A sink that effectfully folds its input chunks with the provided function
1024
+ * and initial state. `f` must preserve chunking-invariance.
1025
+ *
1026
+ * @tsplus static fncts.io.SinkOps foldLeftChunksIO
1027
+ */
1028
+ export function makeFoldLeftChunksIO<R, E, In, S>(
1029
+ z: Lazy<S>,
1030
+ f: (s: S, inp: Conc<In>) => IO<R, E, S>,
1031
+ __tsplusTrace?: string,
1032
+ ): Sink<R, E, In, In, S> {
1033
+ return Sink.foldChunksIO(z, () => true, f);
1034
+ }
1035
+
1036
+ /**
1037
+ * A sink that effectfully folds its inputs with the provided function and
1038
+ * initial state.
1039
+ *
1040
+ * @tsplus static fncts.io.SinkOps foldLeftIO
1041
+ */
1042
+ export function makeFoldLeftIO<R, E, In, S>(
1043
+ z: Lazy<S>,
1044
+ f: (s: S, inp: In) => IO<R, E, S>,
1045
+ __tsplusTrace?: string,
1046
+ ): Sink<R, E, In, In, S> {
1047
+ return Sink.foldIO(z, () => true, f);
1048
+ }
1049
+
1050
+ /**
1051
+ * A sink that effectfully folds its inputs with the provided function,
1052
+ * termination predicate and initial state.
1053
+ *
1054
+ * @tsplus static fncts.io.SinkOps foldIO
1055
+ */
1056
+ export function makeFoldIO<R, E, In, S>(
1057
+ z: Lazy<S>,
1058
+ contFn: Predicate<S>,
1059
+ f: (s: S, inp: In) => IO<R, E, S>,
1060
+ ): Sink<R, E, In, In, S> {
1061
+ return Sink.defer(new Sink(foldIOReader(z(), contFn, f)));
1062
+ }
1063
+
1064
+ function foldChunkSplitIO<R, E, In, S>(
1065
+ s: S,
1066
+ contFn: (s: S) => boolean,
1067
+ f: (s: S, inp: In) => IO<R, E, S>,
1068
+ chunk: Conc<In>,
1069
+ idx: number,
1070
+ len: number,
1071
+ ): IO<R, E, readonly [S, Maybe<Conc<In>>]> {
1072
+ if (idx === len) {
1073
+ return IO.succeedNow([s, Nothing()]);
1074
+ } else {
1075
+ return f(s, chunk[idx]).flatMap((s1) => {
1076
+ if (contFn(s1)) {
1077
+ return foldChunkSplitIO(s1, contFn, f, chunk, idx + 1, len);
1078
+ } else {
1079
+ return IO.succeedNow([s1, Just(chunk.drop(idx + 1))]);
1080
+ }
1081
+ });
1082
+ }
1083
+ }
1084
+
1085
+ function foldIOReader<R, E, In, S>(
1086
+ s: S,
1087
+ contFn: (s: S) => boolean,
1088
+ f: (s: S, inp: In) => IO<R, E, S>,
1089
+ ): Channel<R, E, Conc<In>, unknown, E, Conc<In>, S> {
1090
+ if (!contFn(s)) {
1091
+ return Channel.succeedNow(s);
1092
+ } else {
1093
+ return Channel.readWith(
1094
+ (inp: Conc<In>) =>
1095
+ Channel.fromIO(foldChunkSplitIO(s, contFn, f, inp, 0, inp.length)).flatMap(([nextS, leftovers]) =>
1096
+ leftovers.match(
1097
+ () => foldIOReader(nextS, contFn, f),
1098
+ (l) => Channel.writeNow(l).as(nextS),
1099
+ ),
1100
+ ),
1101
+ (err: E) => Channel.failNow(err),
1102
+ (_: any) => Channel.succeedNow(_),
1103
+ );
1104
+ }
1105
+ }
1106
+
1107
+ /**
1108
+ * Creates a sink that effectfully folds elements of type `In` into a
1109
+ * structure of type `S` until `max` elements have been folded.
1110
+ *
1111
+ * Like {@link makeFoldWeightedIO}, but with a constant cost function of 1.
1112
+ *
1113
+ * @tsplus static fncts.io.SinkOps foldUntilIO
1114
+ */
1115
+ export function makeFoldUntilIO<R, E, In, S>(
1116
+ z: Lazy<S>,
1117
+ max: Lazy<number>,
1118
+ f: (s: S, inp: In) => IO<R, E, S>,
1119
+ __tsplusTrace?: string,
1120
+ ): Sink<R, E, In, In, S> {
1121
+ return Sink.foldIO<R, E, In, readonly [S, number]>(
1122
+ [z(), 0],
1123
+ ([_, n]) => n < max(),
1124
+ ([o, count], i) => f(o, i).map((s) => [s, count + 1]),
1125
+ ).map(([s]) => s);
1126
+ }
1127
+
1128
+ /**
1129
+ * Creates a sink that folds elements of type `In` into a structure of type
1130
+ * `S`, until `max` worth of elements (determined by the `costFn`) have been
1131
+ * folded.
1132
+ *
1133
+ * The `decompose` function will be used for decomposing elements that cause
1134
+ * an `S` aggregate to cross `max` into smaller elements.
1135
+ *
1136
+ *
1137
+ * Be vigilant with this function, it has to generate "simpler" values or the
1138
+ * fold may never end. A value is considered indivisible if `decompose` yields
1139
+ * the empty chunk or a single-valued chunk. In these cases, there is no other
1140
+ * choice than to yield a value that will cross the threshold.
1141
+ *
1142
+ * The {@link makeFoldWeightedDecomposeIO} allows the decompose function to return a
1143
+ * `IO` value, and consequently it allows the sink to fail.
1144
+ *
1145
+ * @tsplus static fncts.io.SinkOps foldWeightedDecompose
1146
+ */
1147
+ export function makeFoldWeightedDecompose<In, S>(
1148
+ z: Lazy<S>,
1149
+ costFn: (s: S, inp: In) => number,
1150
+ max: Lazy<number>,
1151
+ decompose: (inp: In) => Conc<In>,
1152
+ f: (s: S, inp: In) => S,
1153
+ __tsplusTrace?: string,
1154
+ ): Sink<unknown, never, In, In, S> {
1155
+ return Sink.defer(() => {
1156
+ /**
1157
+ * @tsplus tailRec
1158
+ */
1159
+ function fold(
1160
+ inp: Conc<In>,
1161
+ s: S,
1162
+ max: number,
1163
+ dirty: boolean,
1164
+ cost: number,
1165
+ idx: number,
1166
+ ): readonly [S, number, boolean, Conc<In>] {
1167
+ if (idx === inp.length) {
1168
+ return [s, cost, dirty, Conc.empty()];
1169
+ } else {
1170
+ const elem = inp[idx];
1171
+ const total = cost + costFn(s, elem);
1172
+
1173
+ if (total <= max) {
1174
+ return fold(inp, f(s, elem), max, true, total, idx + 1);
1175
+ } else {
1176
+ const decomposed = decompose(elem);
1177
+
1178
+ if (decomposed.length <= 1 && !dirty) {
1179
+ return [f(s, elem), total, true, inp.drop(idx + 1)];
1180
+ } else if (decomposed.length <= 1 && dirty) {
1181
+ return [s, cost, dirty, inp.drop(idx)];
1182
+ } else {
1183
+ return fold(decomposed.concat(inp.drop(idx + 1)), s, max, dirty, cost, 0);
1184
+ }
1185
+ }
1186
+ }
1187
+ }
1188
+ function go(
1189
+ s: S,
1190
+ cost: number,
1191
+ dirty: boolean,
1192
+ max: number,
1193
+ ): Channel<unknown, never, Conc<In>, unknown, never, Conc<In>, S> {
1194
+ return Channel.readWith(
1195
+ (inp: Conc<In>) => {
1196
+ const [nextS, nextCost, nextDirty, leftovers] = fold(inp, s, max, dirty, cost, 0);
1197
+
1198
+ if (leftovers.isNonEmpty) {
1199
+ return Channel.writeNow(leftovers) > Channel.succeedNow(nextS);
1200
+ } else if (cost > max) {
1201
+ return Channel.succeedNow(nextS);
1202
+ } else {
1203
+ return go(nextS, nextCost, nextDirty, max);
1204
+ }
1205
+ },
1206
+ (err: never) => Channel.failNow(err),
1207
+ (_: any) => Channel.succeedNow(s),
1208
+ );
1209
+ }
1210
+
1211
+ return new Sink(go(z(), 0, false, max()));
1212
+ });
1213
+ }
1214
+
1215
+ /**
1216
+ * Creates a sink that effectfully folds elements of type `In` into a
1217
+ * structure of type `S`, until `max` worth of elements (determined by the
1218
+ * `costFn`) have been folded.
1219
+ *
1220
+ * The `decompose` function will be used for decomposing elements that cause
1221
+ * an `S` aggregate to cross `max` into smaller elements. Be vigilant with
1222
+ * this function, it has to generate "simpler" values or the fold may never
1223
+ * end. A value is considered indivisible if `decompose` yields the empty
1224
+ * chunk or a single-valued chunk. In these cases, there is no other choice
1225
+ * than to yield a value that will cross the threshold.
1226
+ *
1227
+ * @tsplus static fncts.io.SinkOps foldWeightedDecomposeIO
1228
+ */
1229
+ export function makeFoldWeightedDecomposeIO<R, E, In, S, R1, E1, R2, E2>(
1230
+ z: Lazy<S>,
1231
+ costFn: (s: S, inp: In) => IO<R1, E1, number>,
1232
+ max: Lazy<number>,
1233
+ decompose: (inp: In) => IO<R2, E2, Conc<In>>,
1234
+ f: (s: S, inp: In) => IO<R, E, S>,
1235
+ __tsplusTrace?: string,
1236
+ ): Sink<R & R1 & R2, E | E1 | E2, In, In, S> {
1237
+ return Sink.defer(() => {
1238
+ function fold(
1239
+ inp: Conc<In>,
1240
+ s: S,
1241
+ max: number,
1242
+ dirty: boolean,
1243
+ cost: number,
1244
+ idx: number,
1245
+ ): IO<R & R1 & R2, E | E1 | E2, readonly [S, number, boolean, Conc<In>]> {
1246
+ if (idx === inp.length) {
1247
+ return IO.succeedNow([s, cost, dirty, Conc.empty()]);
1248
+ } else {
1249
+ const elem = inp[idx];
1250
+ return costFn(s, elem)
1251
+ .map((_) => cost + _)
1252
+ .flatMap((total) => {
1253
+ if (total <= max) {
1254
+ return f(s, elem).flatMap((s) => fold(inp, s, max, true, total, idx + 1));
1255
+ } else {
1256
+ return decompose(elem).flatMap((decomposed) => {
1257
+ if (decomposed.length <= 1 && !dirty) {
1258
+ return f(s, elem).map((s) => [s, total, true, inp.drop(idx + 1)]);
1259
+ } else if (decomposed.length <= 1 && dirty) {
1260
+ return IO.succeedNow([s, cost, dirty, inp.drop(idx)]);
1261
+ } else {
1262
+ return fold(decomposed.concat(inp.drop(idx + 1)), s, max, dirty, cost, 0);
1263
+ }
1264
+ });
1265
+ }
1266
+ });
1267
+ }
1268
+ }
1269
+ function go(
1270
+ s: S,
1271
+ cost: number,
1272
+ dirty: boolean,
1273
+ max: number,
1274
+ ): Channel<R & R1 & R2, E | E1 | E2, Conc<In>, unknown, E | E1 | E2, Conc<In>, S> {
1275
+ return Channel.readWith(
1276
+ (inp: Conc<In>) =>
1277
+ Channel.fromIO(fold(inp, s, max, dirty, cost, 0)).flatMap(([nextS, nextCost, nextDirty, leftovers]) => {
1278
+ if (leftovers.isNonEmpty) {
1279
+ return Channel.writeNow(leftovers) > Channel.succeedNow(nextS);
1280
+ } else if (cost > max) {
1281
+ return Channel.succeedNow(nextS);
1282
+ } else {
1283
+ return go(nextS, nextCost, nextDirty, max);
1284
+ }
1285
+ }),
1286
+ (err: E | E1 | E2) => Channel.failNow(err),
1287
+ (_: any) => Channel.succeedNow(s),
1288
+ );
1289
+ }
1290
+
1291
+ return new Sink(go(z(), 0, false, max()));
1292
+ });
1293
+ }
1294
+
1295
+ /**
1296
+ * Creates a sink that folds elements of type `In` into a structure of type
1297
+ * `S`, until `max` worth of elements (determined by the `costFn`) have been
1298
+ * folded.
1299
+ *
1300
+ * @note
1301
+ * Elements that have an individual cost larger than `max` will force the
1302
+ * sink to cross the `max` cost. See {@link makeFoldWeightedDecompose} for a variant
1303
+ * that can handle these cases.
1304
+ *
1305
+ * @tsplus static fncts.io.SinkOps foldWeighted
1306
+ */
1307
+ export function makeFoldWeighted<In, S>(
1308
+ z: Lazy<S>,
1309
+ costFn: (s: S, inp: In) => number,
1310
+ max: Lazy<number>,
1311
+ f: (s: S, inp: In) => S,
1312
+ __tsplusTrace?: string,
1313
+ ): Sink<unknown, never, In, In, S> {
1314
+ return Sink.foldWeightedDecompose(z, costFn, max, Conc.single, f);
1315
+ }
1316
+
1317
+ /**
1318
+ * Creates a sink that effectfully folds elements of type `In` into a
1319
+ * structure of type `S`, until `max` worth of elements (determined by the
1320
+ * `costFn`) have been folded.
1321
+ *
1322
+ * @note
1323
+ * Elements that have an individual cost larger than `max` will force the
1324
+ * sink to cross the `max` cost. See {@link makeFoldWeightedDecomposeIO} for a
1325
+ * variant that can handle these cases.
1326
+ *
1327
+ * @tsplus static fncts.io.SinkOps foldWeightedIO
1328
+ */
1329
+ export function makeFoldWeightedIO<R, E, In, S, R1, E1>(
1330
+ z: Lazy<S>,
1331
+ costFn: (s: S, inp: In) => IO<R, E, number>,
1332
+ max: Lazy<number>,
1333
+ f: (s: S, inp: In) => IO<R1, E1, S>,
1334
+ __tsplusTrace?: string,
1335
+ ): Sink<R & R1, E | E1, In, In, S> {
1336
+ return Sink.foldWeightedDecomposeIO(z, costFn, max, (inp) => IO.succeedNow(Conc.single(inp)), f);
1337
+ }
1338
+
1339
+ /**
1340
+ * Transforms this sink's result.
1341
+ *
1342
+ * @tsplus fluent fncts.io.Sink map
1343
+ */
1344
+ export function map_<R, E, In, L, Z, Z1>(
1345
+ self: Sink<R, E, In, L, Z>,
1346
+ f: (z: Z) => Z1,
1347
+ __tsplusTrace?: string,
1348
+ ): Sink<R, E, In, L, Z1> {
1349
+ return new Sink(self.channel.map(f));
1350
+ }
1351
+
1352
+ /**
1353
+ * Transforms the errors emitted by this sink using `f`.
1354
+ *
1355
+ * @tsplus fluent fncts.io.Sink mapError
1356
+ */
1357
+ export function mapError_<R, E, In, L, Z, E1>(
1358
+ self: Sink<R, E, In, L, Z>,
1359
+ f: (e: E) => E1,
1360
+ __tsplusTrace?: string,
1361
+ ): Sink<R, E1, In, L, Z> {
1362
+ return new Sink(self.channel.mapError(f));
1363
+ }
1364
+
1365
+ /**
1366
+ * Effectfully transforms this sink's result.
1367
+ *
1368
+ * @tsplus fluent fncts.io.Sink mapIO
1369
+ */
1370
+ export function mapIO_<R, E, In, L, Z, R1, E1, Z1>(
1371
+ self: Sink<R, E, In, L, Z>,
1372
+ f: (z: Z) => IO<R1, E1, Z1>,
1373
+ __tsplusTrace?: string,
1374
+ ): Sink<R & R1, E | E1, In, L, Z1> {
1375
+ return new Sink(self.channel.mapIO(f));
1376
+ }
1377
+
1378
+ /**
1379
+ * Runs this sink until it yields a result, then uses that result to create
1380
+ * another sink from the provided function which will continue to run until it
1381
+ * yields a result.
1382
+ *
1383
+ * This function essentially runs sinks in sequence.
1384
+ *
1385
+ * @tsplus fluent fncts.io.Sink matchSink
1386
+ */
1387
+ export function matchSink_<R, E, In, L, Z, R1, E1, In1 extends In, L1, Z1, R2, E2, In2 extends In, L2, Z2>(
1388
+ self: Sink<R, E, In, L, Z>,
1389
+ onFailure: (e: E) => Sink<R1, E1, In1, L1, Z1>,
1390
+ onSuccess: (z: Z) => Sink<R2, E2, In2, L2, Z2>,
1391
+ __tsplusTrace?: string,
1392
+ ): Sink<R & R1 & R2, E1 | E2, In1 & In2, L | L1 | L2, Z1 | Z2> {
1393
+ return new Sink<R & R1 & R2, E1 | E2, In1 & In2, L | L1 | L2, Z1 | Z2>(
1394
+ self.channel.doneCollect.matchChannel(
1395
+ (e) => onFailure(e).channel,
1396
+ ([leftovers, z]) =>
1397
+ Channel.defer(() => {
1398
+ const leftoversRef = new AtomicReference(leftovers.filter((c) => c.isNonEmpty));
1399
+ const refReader = Channel.succeed(leftoversRef.getAndSet(Conc.empty())).flatMap((chunk) =>
1400
+ Channel.writeChunk(chunk as unknown as Conc<Conc<In1 & In2>>),
1401
+ );
1402
+ const passthrough = Channel.id<never, Conc<In1 & In2>, unknown>();
1403
+ const continuationSink = (refReader > passthrough).pipeTo(onSuccess(z).channel);
1404
+ return continuationSink.doneCollect.flatMap(
1405
+ ([newLeftovers, z1]) =>
1406
+ Channel.succeed(leftoversRef.get).flatMap(Channel.writeChunk) > Channel.writeChunk(newLeftovers).as(z1),
1407
+ );
1408
+ }),
1409
+ ),
1410
+ );
1411
+ }
1412
+
1413
+ /**
1414
+ * Switch to another sink in case of failure
1415
+ *
1416
+ * @tsplus fluent fncts.io.Sink orElse
1417
+ */
1418
+ export function orElse<R, E, In, L, Z, R1, E1, In1, L1, Z1>(
1419
+ self: Sink<R, E, In, L, Z>,
1420
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
1421
+ __tsplusTrace?: string,
1422
+ ): Sink<R & R1, E | E1, In & In1, L | L1, Z | Z1> {
1423
+ return Sink.defer(new Sink<R & R1, E | E1, In & In1, L | L1, Z | Z1>(self.channel.orElse(that().channel)));
1424
+ }
1425
+
1426
+ /**
1427
+ * Provides the sink with its required environment, which eliminates its
1428
+ * dependency on `R`.
1429
+ *
1430
+ * @tsplus fluent fncts.io.Sink provideEnvironment
1431
+ */
1432
+ export function provideEnvironment<R, E, In, L, Z>(
1433
+ self: Sink<R, E, In, L, Z>,
1434
+ r: Lazy<Environment<R>>,
1435
+ __tsplusTrace?: string,
1436
+ ): Sink<unknown, E, In, L, Z> {
1437
+ return new Sink(self.channel.provideEnvironment(r));
1438
+ }
1439
+
1440
+ /**
1441
+ * Runs both sinks in parallel on the input, returning the result or the
1442
+ * error from the one that finishes first.
1443
+ *
1444
+ * @tsplus fluent fncts.io.Sink race
1445
+ */
1446
+ export function race<R, E, In, L, Z, R1, E1, In1, L1, Z1>(
1447
+ self: Sink<R, E, In, L, Z>,
1448
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
1449
+ __tsplusTrace?: string,
1450
+ ): Sink<R & R1, E | E1, In & In1, L | L1, Z | Z1> {
1451
+ return self.raceBoth(that).map((result) => result.value);
1452
+ }
1453
+
1454
+ /**
1455
+ * Runs both sinks in parallel on the input, returning the result or the error
1456
+ * from the one that finishes first.
1457
+ *
1458
+ * @tsplus fluent fncts.io.Sink raceBoth
1459
+ */
1460
+ export function raceBoth<R, E, In, L, Z, R1, E1, In1, L1, Z1>(
1461
+ self: Sink<R, E, In, L, Z>,
1462
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
1463
+ capacity: Lazy<number> = () => 16,
1464
+ __tsplusTrace?: string,
1465
+ ): Sink<R & R1, E | E1, In & In1, L | L1, Either<Z, Z1>> {
1466
+ return self.raceWith(
1467
+ that,
1468
+ (selfDone) => MergeDecision.Done(IO.fromExitNow(selfDone).map(Either.left)),
1469
+ (thatDone) => MergeDecision.Done(IO.fromExitNow(thatDone).map(Either.right)),
1470
+ capacity,
1471
+ );
1472
+ }
1473
+
1474
+ /**
1475
+ * Runs both sinks in parallel on the input, using the specified merge
1476
+ * function as soon as one result or the other has been computed.
1477
+ *
1478
+ * @tsplus fluent fncts.io.Sink raceWith
1479
+ */
1480
+ export function raceWith<R, E, In, L, Z, R1, E1, In1, L1, Z1, R2, E2, Z2, R3, E3, Z3>(
1481
+ self: Sink<R, E, In, L, Z>,
1482
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
1483
+ leftDone: (exit: Exit<E, Z>) => MergeDecision<R1, E1, Z1, E2, Z2>,
1484
+ rightDone: (exit: Exit<E1, Z1>) => MergeDecision<R, E, Z, E3, Z3>,
1485
+ capacity: Lazy<number> = () => 16,
1486
+ __tsplusTrace?: string,
1487
+ ): Sink<R & R1 & R2 & R3, E2 | E3, In & In1, L | L1, Z2 | Z3> {
1488
+ const scoped = IO.defer(() => {
1489
+ const that0 = that();
1490
+ const capacity0 = capacity();
1491
+ return Do((_) => {
1492
+ const hub = _(Hub.makeBounded<Either<Exit<never, any>, Conc<In & In1>>>(capacity()));
1493
+ const c1 = _(Channel.fromHubScoped(hub));
1494
+ const c2 = _(Channel.fromHubScoped(hub));
1495
+ const reader = Channel.toHub(hub);
1496
+ const writer = c1.pipeTo(self.channel).mergeWith(c2.pipeTo(that0.channel), leftDone, rightDone);
1497
+ const channel = reader.mergeWith(
1498
+ writer,
1499
+ () => MergeDecision.Await(IO.fromExitNow),
1500
+ (done) => MergeDecision.Done(IO.fromExitNow(done)),
1501
+ );
1502
+ return new Sink<R & R1 & R2 & R3, E2 | E3, In & In1, L | L1, Z2 | Z3>(channel);
1503
+ });
1504
+ });
1505
+ return Sink.unwrapScoped(scoped);
1506
+ }
1507
+
1508
+ /**
1509
+ * Accesses the specified service in the environment of the effect.
1510
+ *
1511
+ * @tsplus static fncts.io.SinkOps service
1512
+ */
1513
+ export function service<S>(/** @tsplus auto */ tag: Tag<S>): Sink<Has<S>, never, unknown, never, S> {
1514
+ return Sink.serviceWith(Function.identity);
1515
+ }
1516
+
1517
+ /**
1518
+ * Accesses the specified service in the environment of the sink.
1519
+ *
1520
+ * @tsplus static fncts.io.SinkOps serviceWith
1521
+ */
1522
+ export function serviceWith<S, Z>(
1523
+ f: (service: S) => Z,
1524
+ /** @tsplus auto */ tag: Tag<S>,
1525
+ ): Sink<Has<S>, never, unknown, never, Z> {
1526
+ return Sink.fromIO(IO.serviceWith(f, tag));
1527
+ }
1528
+
1529
+ /**
1530
+ * Accesses the specified service in the environment of the sink in the
1531
+ * context of an effect.
1532
+ *
1533
+ * @tsplus static fncts.io.SinkOps serviceWithIO
1534
+ */
1535
+ export function serviceWithIO<S, R, E, Z>(
1536
+ f: (service: S) => IO<R, E, Z>,
1537
+ /** @tsplus auto */ tag: Tag<S>,
1538
+ ): Sink<Has<S> & R, E, unknown, never, Z> {
1539
+ return Sink.fromIO(IO.serviceWithIO(f, tag));
1540
+ }
1541
+
1542
+ /**
1543
+ * Accesses the specified service in the environment of the sink in the
1544
+ * context of a sink.
1545
+ *
1546
+ * @tsplus static fncts.io.SinkOps serviceWithSink
1547
+ */
1548
+ export function serviceWithSink<S, R, E, In, L, Z>(
1549
+ f: (service: S) => Sink<R, E, In, L, Z>,
1550
+ /** @tsplus auto */ tag: Tag<S>,
1551
+ ): Sink<Has<S> & R, E, In, L, Z> {
1552
+ return new Sink(
1553
+ Channel.unwrap(
1554
+ IO.serviceWith(
1555
+ f.compose((s) => s.channel),
1556
+ tag,
1557
+ ),
1558
+ ),
1559
+ );
1560
+ }
1561
+
1562
+ /**
1563
+ * Splits the sink on the specified predicate, returning a new sink that
1564
+ * consumes elements until an element after the first satisfies the specified
1565
+ * predicate.
1566
+ *
1567
+ * @tsplus fluent fncts.io.Sink splitWhere
1568
+ */
1569
+ export function splitWhere<R, E, In, L extends In, Z>(
1570
+ self: Sink<R, E, In, L, Z>,
1571
+ p: Predicate<In>,
1572
+ __tsplusTrace?: string,
1573
+ ): Sink<R, E, In, In, Z> {
1574
+ return new Sink(
1575
+ Channel.fromIO(Ref.make<Conc<In>>(Conc.empty())).flatMap((ref) =>
1576
+ splitter<R, E, In>(p, false, ref)
1577
+ .pipeToOrFail(self.channel)
1578
+ .collectElements.flatMap(([leftovers, z]) =>
1579
+ Channel.fromIO(ref.get).flatMap(
1580
+ (leftover) => Channel.writeNow(leftover.concat(leftovers.flatten)) > Channel.succeedNow(z),
1581
+ ),
1582
+ ),
1583
+ ),
1584
+ );
1585
+ }
1586
+
1587
+ function splitter<R, E, In>(
1588
+ p: Predicate<In>,
1589
+ written: boolean,
1590
+ leftovers: Ref<Conc<In>>,
1591
+ ): Channel<R, never, Conc<In>, unknown, E, Conc<In>, unknown> {
1592
+ return Channel.readWithCause(
1593
+ (inp) => {
1594
+ if (inp.isEmpty) {
1595
+ return splitter(p, written, leftovers);
1596
+ } else if (written) {
1597
+ const index = inp.findIndex(p);
1598
+ if (index === -1) {
1599
+ return Channel.writeNow(inp) > splitter<R, E, In>(p, true, leftovers);
1600
+ } else {
1601
+ const [left, right] = inp.splitAt(index);
1602
+ return Channel.writeNow(left) > Channel.fromIO(leftovers.set(right));
1603
+ }
1604
+ } else {
1605
+ const index = inp.findIndex(p);
1606
+ if (index === -1) {
1607
+ return Channel.writeNow(inp) > splitter<R, E, In>(p, true, leftovers);
1608
+ } else {
1609
+ const [left, right] = inp.splitAt(Math.max(index, 1));
1610
+ return Channel.writeNow(left) > Channel.fromIO(leftovers.set(right));
1611
+ }
1612
+ }
1613
+ },
1614
+ Channel.failCauseNow,
1615
+ Channel.succeedNow,
1616
+ );
1617
+ }
1618
+
1619
+ /**
1620
+ * A sink that immediately ends with the specified value.
1621
+ *
1622
+ * @tsplus static fncts.io.SinkOps succeed
1623
+ */
1624
+ export function succeed<Z>(z: Lazy<Z>, __tsplusTrace?: string): Sink<unknown, never, unknown, never, Z> {
1625
+ return new Sink(Channel.succeed(z));
1626
+ }
1627
+
1628
+ /**
1629
+ * A sink that immediately ends with the specified value.
1630
+ *
1631
+ * @tsplus static fncts.io.SinkOps succeedNow
1632
+ */
1633
+ export function succeedNow<Z>(z: Z, __tsplusTrace?: string): Sink<unknown, never, unknown, never, Z> {
1634
+ return new Sink(Channel.succeedNow(z));
1635
+ }
1636
+
1637
+ /**
1638
+ * Summarize a sink by running an effect when the sink starts and again when
1639
+ * it completes
1640
+ *
1641
+ * @tsplus fluent fncts.io.Sink summarized
1642
+ */
1643
+ export function summarized<R, E, In, L, Z, R1, E1, B, C>(
1644
+ self: Sink<R, E, In, L, Z>,
1645
+ summary: Lazy<IO<R1, E1, B>>,
1646
+ f: (b1: B, b2: B) => C,
1647
+ __tsplusTrace?: string,
1648
+ ): Sink<R & R1, E | E1, In, L, readonly [Z, C]> {
1649
+ return new Sink(
1650
+ Channel.unwrap(
1651
+ IO.succeed(summary).map((summary) =>
1652
+ Channel.fromIO(summary).flatMap((start) =>
1653
+ self.channel.flatMap((done) => Channel.fromIO(summary).map((end) => [done, f(start, end)])),
1654
+ ),
1655
+ ),
1656
+ ),
1657
+ );
1658
+ }
1659
+
1660
+ /**
1661
+ * @tsplus getter fncts.io.Sink timed
1662
+ */
1663
+ export function timed<R, E, In, L, Z>(
1664
+ self: Sink<R, E, In, L, Z>,
1665
+ __tsplusTrace?: string,
1666
+ ): Sink<R, E, In, L, readonly [Z, Duration]> {
1667
+ return self.summarized(Clock.currentTime, (start, end) => Duration.fromInterval(start, end));
1668
+ }
1669
+
1670
+ /**
1671
+ * Creates a sink produced from an effect.
1672
+ *
1673
+ * @tsplus static fncts.io.SinkOps unwrap
1674
+ */
1675
+ export function unwrap<R, E, R1, E1, In, L, Z>(
1676
+ io: Lazy<IO<R, E, Sink<R1, E1, In, L, Z>>>,
1677
+ ): Sink<R & R1, E | E1, In, L, Z> {
1678
+ return new Sink(Channel.unwrap(io().map((sink) => sink.channel)));
1679
+ }
1680
+
1681
+ /**
1682
+ * Creates a sink produced from a scoped effect.
1683
+ *
1684
+ * @tsplus static fncts.io.SinkOps unwrapScoped
1685
+ */
1686
+ export function unwrapScoped<R, E, R1, E1, In, L, Z>(
1687
+ scoped: Lazy<IO<Has<Scope> & R, E, Sink<R1, E1, In, L, Z>>>,
1688
+ __tsplusTrace?: string,
1689
+ ): Sink<R & R1, E | E1, In, L, Z> {
1690
+ return new Sink(Channel.unwrapScoped(scoped().map((sink) => sink.channel)));
1691
+ }
1692
+
1693
+ /**
1694
+ * Feeds inputs to this sink until it yields a result, then switches over to
1695
+ * the provided sink until it yields a result, finally combining the two
1696
+ * results into a tuple.
1697
+ *
1698
+ * @tsplus fluent fncts.io.Sink zip
1699
+ */
1700
+ export function zip<R, E, In, L, Z, R1, E1, In1 extends In, L1 extends L, Z1>(
1701
+ self: Sink<R, E, In, L, Z>,
1702
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
1703
+ __tsplusTrace?: string,
1704
+ ): Sink<R & R1, E | E1, In & In1, L | L1, readonly [Z, Z1]> {
1705
+ return self.zipWith(that, Function.tuple);
1706
+ }
1707
+
1708
+ /**
1709
+ * Runs both sinks in parallel on the input and combines the results in a
1710
+ * tuple.
1711
+ *
1712
+ * @tsplus fluent fncts.io.Sink zipC
1713
+ */
1714
+ export function zipC<R, E, In, L, Z, R1, E1, In1 extends In, L1 extends L, Z1>(
1715
+ self: Sink<R, E, In, L, Z>,
1716
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
1717
+ __tsplusTrace?: string,
1718
+ ): Sink<R & R1, E | E1, In & In1, L | L1, readonly [Z, Z1]> {
1719
+ return self.zipWithC(that, Function.tuple);
1720
+ }
1721
+
1722
+ /**
1723
+ * Feeds inputs to this sink until it yields a result, then switches over to
1724
+ * the provided sink until it yields a result, finally combining the two
1725
+ * results with `f`.
1726
+ *
1727
+ * @tsplus fluent fncts.io.Sink zipWith
1728
+ */
1729
+ export function zipWith<R, E, In, L, Z, R1, E1, In1 extends In, L1 extends L, Z1, Z2>(
1730
+ self: Lazy<Sink<R, E, In, L, Z>>,
1731
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
1732
+ f: (z: Z, z1: Z1) => Z2,
1733
+ __tsplusTrace?: string,
1734
+ ): Sink<R & R1, E | E1, In & In1, L | L1, Z2> {
1735
+ return Sink.defer(self().flatMap((z) => that().map((z1) => f(z, z1))));
1736
+ }
1737
+
1738
+ /**
1739
+ * Runs both sinks in parallel on the input and combines the results using the
1740
+ * provided function.
1741
+ *
1742
+ * @tsplus fluent fncts.io.Sink zipWithC
1743
+ */
1744
+ export function zipWithC<R, E, In, L, Z, R1, E1, In1, L1, Z1, Z2>(
1745
+ self: Lazy<Sink<R, E, In, L, Z>>,
1746
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
1747
+ f: (z: Z, z1: Z1) => Z2,
1748
+ __tsplusTrace?: string,
1749
+ ): Sink<R & R1, E | E1, In & In1, L | L1, Z2> {
1750
+ return Sink.defer(
1751
+ self().raceWith(
1752
+ that(),
1753
+ (exit) =>
1754
+ exit.match(
1755
+ (err) => MergeDecision.Done(IO.failCauseNow(err)),
1756
+ (lz) =>
1757
+ MergeDecision.Await((exit) =>
1758
+ exit.match(
1759
+ (cause) => IO.failCauseNow(cause),
1760
+ (rz) => IO.succeedNow(f(lz, rz)),
1761
+ ),
1762
+ ),
1763
+ ),
1764
+ (exit) =>
1765
+ exit.match(
1766
+ (err) => MergeDecision.Done(IO.failCauseNow(err)),
1767
+ (rz) =>
1768
+ MergeDecision.Await((exit) =>
1769
+ exit.match(
1770
+ (cause) => IO.failCauseNow(cause),
1771
+ (lz) => IO.succeedNow(f(lz, rz)),
1772
+ ),
1773
+ ),
1774
+ ),
1775
+ ),
86
1776
  );
87
- return new Sink(process);
88
1777
  }