@fncts/io 0.0.9 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/Channel/api.d.ts +27 -4
  2. package/Channel/internal/MergeDecision.d.ts +14 -0
  3. package/STM/api.d.ts +7 -0
  4. package/Sink/api.d.ts +455 -7
  5. package/TReentrantLock/api.d.ts +97 -0
  6. package/TReentrantLock/definition.d.ts +65 -0
  7. package/TReentrantLock.d.ts +2 -0
  8. package/_cjs/Cached/api.cjs +1 -1
  9. package/_cjs/Cached/api.cjs.map +1 -1
  10. package/_cjs/Channel/api/mapOutIOC.cjs +1 -1
  11. package/_cjs/Channel/api/mapOutIOC.cjs.map +1 -1
  12. package/_cjs/Channel/api/mergeAllWith.cjs +2 -2
  13. package/_cjs/Channel/api/mergeAllWith.cjs.map +1 -1
  14. package/_cjs/Channel/api/mergeWith.cjs +1 -1
  15. package/_cjs/Channel/api/mergeWith.cjs.map +1 -1
  16. package/_cjs/Channel/api.cjs +87 -32
  17. package/_cjs/Channel/api.cjs.map +1 -1
  18. package/_cjs/Channel/internal/MergeDecision.cjs +11 -2
  19. package/_cjs/Channel/internal/MergeDecision.cjs.map +1 -1
  20. package/_cjs/IO/api/foreachC.cjs +2 -2
  21. package/_cjs/IO/api/foreachC.cjs.map +1 -1
  22. package/_cjs/STM/api.cjs +15 -6
  23. package/_cjs/STM/api.cjs.map +1 -1
  24. package/_cjs/Sink/api.cjs +1180 -40
  25. package/_cjs/Sink/api.cjs.map +1 -1
  26. package/_cjs/Stream/api.cjs +28 -28
  27. package/_cjs/Stream/api.cjs.map +1 -1
  28. package/_cjs/TReentrantLock/api.cjs +297 -0
  29. package/_cjs/TReentrantLock/api.cjs.map +1 -0
  30. package/_cjs/TReentrantLock/definition.cjs +125 -0
  31. package/_cjs/TReentrantLock/definition.cjs.map +1 -0
  32. package/_cjs/TReentrantLock.cjs +32 -0
  33. package/_cjs/TReentrantLock.cjs.map +1 -0
  34. package/_cjs/collection/immutable/Conc/filterIO.cjs +35 -0
  35. package/_cjs/collection/immutable/Conc/filterIO.cjs.map +1 -0
  36. package/_cjs/collection/immutable/Conc.cjs +13 -0
  37. package/_cjs/collection/immutable/Conc.cjs.map +1 -1
  38. package/_mjs/Cached/api.mjs +1 -1
  39. package/_mjs/Cached/api.mjs.map +1 -1
  40. package/_mjs/Channel/api/mapOutIOC.mjs +1 -1
  41. package/_mjs/Channel/api/mapOutIOC.mjs.map +1 -1
  42. package/_mjs/Channel/api/mergeAllWith.mjs +2 -2
  43. package/_mjs/Channel/api/mergeAllWith.mjs.map +1 -1
  44. package/_mjs/Channel/api/mergeWith.mjs +1 -1
  45. package/_mjs/Channel/api/mergeWith.mjs.map +1 -1
  46. package/_mjs/Channel/api.mjs +75 -30
  47. package/_mjs/Channel/api.mjs.map +1 -1
  48. package/_mjs/Channel/internal/MergeDecision.mjs +7 -0
  49. package/_mjs/Channel/internal/MergeDecision.mjs.map +1 -1
  50. package/_mjs/IO/api/foreachC.mjs +2 -2
  51. package/_mjs/IO/api/foreachC.mjs.map +1 -1
  52. package/_mjs/STM/api.mjs +13 -6
  53. package/_mjs/STM/api.mjs.map +1 -1
  54. package/_mjs/Sink/api.mjs +996 -31
  55. package/_mjs/Sink/api.mjs.map +1 -1
  56. package/_mjs/Stream/api.mjs +28 -28
  57. package/_mjs/Stream/api.mjs.map +1 -1
  58. package/_mjs/TReentrantLock/api.mjs +243 -0
  59. package/_mjs/TReentrantLock/api.mjs.map +1 -0
  60. package/_mjs/TReentrantLock/definition.mjs +102 -0
  61. package/_mjs/TReentrantLock/definition.mjs.map +1 -0
  62. package/_mjs/TReentrantLock.mjs +4 -0
  63. package/_mjs/TReentrantLock.mjs.map +1 -0
  64. package/_mjs/collection/immutable/Conc/filterIO.mjs +22 -0
  65. package/_mjs/collection/immutable/Conc/filterIO.mjs.map +1 -0
  66. package/_mjs/collection/immutable/Conc.mjs +1 -0
  67. package/_mjs/collection/immutable/Conc.mjs.map +1 -1
  68. package/_src/Channel/api.ts +98 -11
  69. package/_src/Channel/internal/MergeDecision.ts +15 -0
  70. package/_src/IO/api.ts +1 -1
  71. package/_src/STM/api.ts +9 -0
  72. package/_src/Sink/api.ts +1350 -19
  73. package/_src/TFuture/definition.ts +1 -1
  74. package/_src/TReentrantLock/api.ts +193 -0
  75. package/_src/TReentrantLock/definition.ts +86 -0
  76. package/_src/TReentrantLock.ts +4 -0
  77. package/_src/collection/immutable/Conc/filterIO.ts +16 -0
  78. package/_src/collection/immutable/Conc.ts +1 -0
  79. package/collection/immutable/Conc/filterIO.d.ts +7 -0
  80. package/collection/immutable/Conc.d.ts +1 -0
  81. package/package.json +3 -3
package/_src/Sink/api.ts CHANGED
@@ -1,18 +1,214 @@
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
+ * @tsplus fluent fncts.io.Sink apFirst
7
+ */
8
+ export function apFirst<R, E, In, L, Z, R1, E1, In1 extends In, L1 extends L, Z1>(
9
+ self: Sink<R, E, In, L, Z>,
10
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
11
+ ): Sink<R & R1, E | E1, In & In1, L | L1, Z> {
12
+ return self.zipWith(that, (z, _) => z);
13
+ }
14
+
15
+ /**
16
+ * @tsplus fluent fncts.io.Sink apFirstC
17
+ */
18
+ export function apFirstC<R, E, In, L, Z, R1, E1, In1 extends In, L1 extends L, Z1>(
19
+ self: Sink<R, E, In, L, Z>,
20
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
21
+ ): Sink<R & R1, E | E1, In & In1, L | L1, Z> {
22
+ return self.zipWithC(that, (z, _) => z);
23
+ }
24
+
25
+ /**
26
+ * @tsplus fluent fncts.io.Sink apSecond
27
+ */
28
+ export function apSecond<R, E, In, L, Z, R1, E1, In1 extends In, L1 extends L, Z1>(
29
+ self: Sink<R, E, In, L, Z>,
30
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
31
+ ): Sink<R & R1, E | E1, In & In1, L | L1, Z1> {
32
+ return self.zipWith(that, (_, z1) => z1);
33
+ }
34
+
35
+ /**
36
+ * @tsplus fluent fncts.io.Sink apSecondC
37
+ */
38
+ export function apSecondC<R, E, In, L, Z, R1, E1, In1 extends In, L1 extends L, Z1>(
39
+ self: Sink<R, E, In, L, Z>,
40
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
41
+ ): Sink<R & R1, E | E1, In & In1, L | L1, Z1> {
42
+ return self.zipWithC(that, (_, z1) => z1);
43
+ }
44
+
45
+ /**
46
+ * @tsplus fluent fncts.io.Sink as
47
+ */
48
+ export function as<R, E, In, L, Z, Z1>(self: Sink<R, E, In, L, Z>, z: Lazy<Z1>): Sink<R, E, In, L, Z1> {
49
+ return self.map(() => z());
50
+ }
51
+
52
+ /**
53
+ * @tsplus fluent fncts.io.Sink collectAll
54
+ */
55
+ export function collectAll<R, E, In extends L, L, Z>(
56
+ self: Sink<R, E, In, L, Z>,
57
+ __tsplusTrace?: string,
58
+ ): Sink<R, E, In, L, Conc<Z>> {
59
+ return self.collectAllWhileWith(
60
+ Conc.empty<Z>(),
61
+ () => true,
62
+ (s, z) => s.append(z),
6
63
  );
7
64
  }
8
65
 
9
66
  /**
10
- * A sink that collects all of its inputs into a chunk.
11
- *
12
- * @tsplus static fncts.io.SinkOps collectAll
67
+ * @tsplus fluent fncts.io.Sink collectAllWhileWith
68
+ */
69
+ export function collectAllWhileWith<R, E, In extends L, L, Z, S>(
70
+ self: Sink<R, E, In, L, Z>,
71
+ z: Lazy<S>,
72
+ p: Predicate<Z>,
73
+ f: (s: S, z: Z) => S,
74
+ __tsplusTrace?: string,
75
+ ): Sink<R, E, In, L, S> {
76
+ return new Sink(
77
+ Channel.fromIO(Ref.make<Conc<In>>(Conc.empty()).zip(Ref.make(false))).flatMap(([leftoversRef, upstreamDoneRef]) => {
78
+ const upstreamMarker: Channel<unknown, never, Conc<In>, unknown, never, Conc<In>, unknown> = Channel.readWith(
79
+ (inp) => Channel.writeNow(inp) > upstreamMarker,
80
+ Channel.failNow,
81
+ (x) => Channel.fromIO(upstreamDoneRef.set(true)).as(x),
82
+ );
83
+
84
+ function loop(currentResult: S): Channel<R, never, Conc<In>, unknown, E, Conc<L>, S> {
85
+ return self.channel.collectElements.matchChannel(Channel.failNow, ([leftovers, doneValue]) => {
86
+ if (p(doneValue)) {
87
+ return (
88
+ Channel.fromIO(leftoversRef.set(leftovers.flatten as Conc<In>)) >
89
+ Channel.fromIO(upstreamDoneRef.get).flatMap((upstreamDone) => {
90
+ const accumulatedResult = f(currentResult, doneValue);
91
+ if (upstreamDone) return Channel.writeNow(leftovers.flatten).as(accumulatedResult);
92
+ else return loop(accumulatedResult);
93
+ })
94
+ );
95
+ } else {
96
+ return Channel.writeNow(leftovers.flatten).as(currentResult);
97
+ }
98
+ });
99
+ }
100
+
101
+ return upstreamMarker.pipeTo(Channel.bufferChunk(leftoversRef)).pipeTo(loop(z()));
102
+ }),
103
+ );
104
+ }
105
+
106
+ /**
107
+ * @tsplus getter fncts.io.Sink collectLeftover
108
+ */
109
+ export function collectLeftover<R, E, In, L, Z>(
110
+ self: Sink<R, E, In, L, Z>,
111
+ __tsplusTrace?: string,
112
+ ): Sink<R, E, In, never, readonly [Z, Conc<L>]> {
113
+ return new Sink(self.channel.collectElements.map(([chunks, z]) => [z, chunks.flatten]));
114
+ }
115
+
116
+ /**
117
+ * @tsplus fluent fncts.io.Sink contramap
118
+ */
119
+ export function contramap<R, E, In, L, Z, In1>(self: Sink<R, E, In, L, Z>, f: (inp: In1) => In): Sink<R, E, In1, L, Z> {
120
+ return self.contramapChunks((chunk) => chunk.map(f));
121
+ }
122
+
123
+ /**
124
+ * @tsplus fluent fncts.io.Sink contramapChunks
125
+ */
126
+ export function contramapChunks<R, E, In, L, Z, In1>(
127
+ self: Sink<R, E, In, L, Z>,
128
+ f: (chunk: Conc<In1>) => Conc<In>,
129
+ ): Sink<R, E, In1, L, Z> {
130
+ const loop: Channel<R, never, Conc<In1>, unknown, never, Conc<In>, unknown> = Channel.readWith(
131
+ (chunk) => Channel.writeNow(f(chunk)) > loop,
132
+ Channel.failNow,
133
+ Channel.succeedNow,
134
+ );
135
+ return new Sink(loop >>> self.channel);
136
+ }
137
+
138
+ /**
139
+ * @tsplus fluent fncts.io.Sink contramapChunksIO
140
+ */
141
+ export function contramapChunksIO<R, E, In, L, Z, R1, E1, In1>(
142
+ self: Sink<R, E, In, L, Z>,
143
+ f: (chunk: Conc<In1>) => IO<R1, E1, Conc<In>>,
144
+ ): Sink<R & R1, E | E1, In1, L, Z> {
145
+ const loop: Channel<R & R1, never, Conc<In1>, unknown, E | E1, Conc<In>, unknown> = Channel.readWith(
146
+ (chunk) => Channel.fromIO(f(chunk)).flatMap(Channel.writeNow) > loop,
147
+ Channel.failNow,
148
+ Channel.succeedNow,
149
+ );
150
+ return new Sink(loop.pipeToOrFail(self.channel));
151
+ }
152
+
153
+ /**
154
+ * @tsplus fluent fncts.io.Sink contramapIO
155
+ */
156
+ export function contramapIO<R, E, In, L, Z, R1, E1, In1>(
157
+ self: Sink<R, E, In, L, Z>,
158
+ f: (inp: In1) => IO<R1, E1, In>,
159
+ ): Sink<R & R1, E | E1, In1, L, Z> {
160
+ return self.contramapChunksIO((chunk) => chunk.mapIO(f));
161
+ }
162
+
163
+ /**
164
+ * @tsplus fluent fncts.io.Sink dimap
13
165
  */
14
- export function collectAll<Err, A>(): Sink<unknown, Err, A, never, Conc<A>> {
15
- return new Sink(collectLoop<Err, A>(Conc.empty()));
166
+ export function dimap<R, E, In, L, Z, In1, Z1>(
167
+ self: Sink<R, E, In, L, Z>,
168
+ f: (inp: In1) => In,
169
+ g: (z: Z) => Z1,
170
+ ): Sink<R, E, In1, L, Z1> {
171
+ return self.contramap(f).map(g);
172
+ }
173
+
174
+ /**
175
+ * @tsplus fluent fncts.io.Sink dimapChunks
176
+ */
177
+ export function dimapChunks<R, E, In, L, Z, In1, Z1>(
178
+ self: Sink<R, E, In, L, Z>,
179
+ f: (chunk: Conc<In1>) => Conc<In>,
180
+ g: (z: Z) => Z1,
181
+ ): Sink<R, E, In1, L, Z1> {
182
+ return self.contramapChunks(f).map(g);
183
+ }
184
+
185
+ /**
186
+ * @tsplus fluent fncts.io.Sink dimapChunksIO
187
+ */
188
+ export function dimapChunksIO<R, E, In, L, Z, R1, E1, In1, R2, E2, Z1>(
189
+ self: Sink<R, E, In, L, Z>,
190
+ f: (chunk: Conc<In1>) => IO<R1, E1, Conc<In>>,
191
+ g: (z: Z) => IO<R2, E2, Z1>,
192
+ ): Sink<R & R1 & R2, E | E1 | E2, In1, L, Z1> {
193
+ return self.contramapChunksIO(f).mapIO(g);
194
+ }
195
+
196
+ /**
197
+ * @tsplus fluent fncts.io.Sink dimapIO
198
+ */
199
+ export function dimapIO<R, E, In, L, Z, R1, E1, In1, R2, E2, Z1>(
200
+ self: Sink<R, E, In, L, Z>,
201
+ f: (inp: In1) => IO<R1, E1, In>,
202
+ g: (z: Z) => IO<R2, E2, Z1>,
203
+ ): Sink<R & R1 & R2, E | E1 | E2, In1, L, Z1> {
204
+ return self.contramapIO(f).mapIO(g);
205
+ }
206
+
207
+ /**
208
+ * @tsplus static fncts.io.SinkOps defer
209
+ */
210
+ export function defer<R, E, In, L, Z>(sink: Lazy<Sink<R, E, In, L, Z>>): Sink<R, E, In, L, Z> {
211
+ return new Sink(Channel.defer(sink().channel));
16
212
  }
17
213
 
18
214
  const drainLoop: Channel<unknown, never, Conc<unknown>, unknown, never, Conc<never>, void> = Channel.readWithCause(
@@ -48,15 +244,361 @@ export function dropWhile<Err, In>(predicate: Predicate<In>): Sink<unknown, neve
48
244
  return new Sink(loop);
49
245
  }
50
246
 
247
+ /**
248
+ * @tsplus static fncts.io.SinkOps environment
249
+ */
250
+ export function environment<R>(__tsplusTrace?: string): Sink<R, never, unknown, never, Environment<R>> {
251
+ return Sink.fromIO(IO.environment<R>());
252
+ }
253
+
254
+ /**
255
+ * @tsplus static fncts.io.SinkOps environmentWith
256
+ */
257
+ export function environmentWith<R, Z>(
258
+ f: (r: Environment<R>) => Z,
259
+ __tsplusTrace?: string,
260
+ ): Sink<R, never, unknown, never, Z> {
261
+ return Sink.fromIO(IO.environmentWith(f));
262
+ }
263
+
264
+ /**
265
+ * @tsplus static fncts.io.SinkOps environmentWithIO
266
+ */
267
+ export function environmentWithIO<R, R1, E, Z>(
268
+ f: (r: Environment<R>) => IO<R1, E, Z>,
269
+ __tsplusTrace?: string,
270
+ ): Sink<R & R1, E, unknown, never, Z> {
271
+ return Sink.fromIO(IO.environmentWithIO(f));
272
+ }
273
+
274
+ /**
275
+ * @tsplus static fncts.io.SinkOps environmentWithSink
276
+ */
277
+ export function environmentWithSink<R, R1, E, In, L, Z>(
278
+ f: (r: Environment<R>) => Sink<R1, E, In, L, Z>,
279
+ __tsplusTrace?: string,
280
+ ): Sink<R & R1, E, In, L, Z> {
281
+ return new Sink(Channel.unwrap(IO.environmentWith(f.compose((s) => s.channel))));
282
+ }
283
+
284
+ /**
285
+ * @tsplus static fncts.io.SinkOps fail
286
+ */
287
+ export function fail<E>(e: Lazy<E>): Sink<unknown, E, unknown, never, never> {
288
+ return new Sink(Channel.fail(e));
289
+ }
290
+
291
+ /**
292
+ * @tsplus static fncts.io.SinkOps failCause
293
+ */
294
+ export function failCause<E>(cause: Lazy<Cause<E>>, __tsplusTrace?: string): Sink<unknown, E, unknown, never, never> {
295
+ return new Sink(Channel.failCause(cause));
296
+ }
297
+
298
+ /**
299
+ * @tsplus static fncts.io.SinkOps failCauseNow
300
+ */
301
+ export function failCauseNow<E>(cause: Cause<E>, __tsplusTrace?: string): Sink<unknown, E, unknown, never, never> {
302
+ return new Sink(Channel.failCauseNow(cause));
303
+ }
304
+
305
+ /**
306
+ * @tsplus static fncts.io.SinkOps failNow
307
+ */
308
+ export function failNow<E>(e: E, __tsplusTrace?: string): Sink<unknown, E, unknown, never, never> {
309
+ return new Sink(Channel.failNow(e));
310
+ }
311
+
312
+ /**
313
+ * @tsplus static fncts.io.SinkOps filterInput
314
+ */
315
+ export function filterInput<R, E, In, L, Z>(
316
+ self: Sink<R, E, In, L, Z>,
317
+ p: Predicate<In>,
318
+ __tsplusTrace?: string,
319
+ ): Sink<R, E, In, L, Z> {
320
+ return self.contramapChunks((chunk) => chunk.filter(p));
321
+ }
322
+
323
+ /**
324
+ * @tsplus static fncts.io.SinkOps filterInputIO
325
+ */
326
+ export function filterInputIO<R, E, In, L, Z, R1, E1>(
327
+ self: Sink<R, E, In, L, Z>,
328
+ p: (inp: In) => IO<R1, E1, boolean>,
329
+ __tsplusTrace?: string,
330
+ ): Sink<R & R1, E | E1, In, L, Z> {
331
+ return self.contramapChunksIO((chunk) => chunk.filterIO(p));
332
+ }
333
+
334
+ /**
335
+ * @tsplus fluent fncts.io.Sink findIO
336
+ */
337
+ export function findIO<R, E, In extends L, L, Z, R1, E1>(
338
+ self: Sink<R, E, In, L, Z>,
339
+ f: (z: Z) => IO<R1, E1, boolean>,
340
+ __tsplusTrace?: string,
341
+ ): Sink<R & R1, E | E1, In, L, Maybe<Z>> {
342
+ return new Sink(
343
+ Channel.fromIO(Ref.make(Conc.empty<In>()).zip(Ref.make(false))).flatMap(([leftoversRef, upstreamDoneRef]) => {
344
+ const upstreamMarker: Channel<unknown, never, Conc<In>, unknown, never, Conc<In>, unknown> = Channel.readWith(
345
+ (inp) => Channel.writeNow(inp) > upstreamMarker,
346
+ Channel.failNow,
347
+ (x) => Channel.fromIO(upstreamDoneRef.set(true)).as(x),
348
+ );
349
+
350
+ const loop: Channel<
351
+ R & R1,
352
+ never,
353
+ Conc<In>,
354
+ unknown,
355
+ E | E1,
356
+ Conc<L>,
357
+ Maybe<Z>
358
+ > = self.channel.collectElements.matchChannel(Channel.failNow, ([leftovers, doneValue]) =>
359
+ Channel.fromIO(f(doneValue)).flatMap(
360
+ (satisfied) =>
361
+ Channel.fromIO(leftoversRef.set(leftovers.flatten as Conc<In>)) >
362
+ Channel.fromIO(upstreamDoneRef.get).flatMap((upstreamDone) => {
363
+ if (satisfied) return Channel.writeNow(leftovers.flatten).as(Just(doneValue));
364
+ else if (upstreamDone) return Channel.writeNow(leftovers.flatten).as(Nothing());
365
+ else return loop;
366
+ }),
367
+ ),
368
+ );
369
+
370
+ return (upstreamMarker >>> Channel.bufferChunk(leftoversRef)) >>> loop;
371
+ }),
372
+ );
373
+ }
374
+
375
+ /**
376
+ * @tsplus fluent fncts.io.Sink flatMap
377
+ */
378
+ export function flatMap<R, E, In, L, Z, R1, E1, In1 extends In, L1, Z1>(
379
+ self: Sink<R, E, In, L, Z>,
380
+ f: (z: Z) => Sink<R1, E1, In1, L1, Z1>,
381
+ __tsplusTrace?: string,
382
+ ): Sink<R & R1, E | E1, In1, L | L1, Z1> {
383
+ return self.matchSink(Sink.failNow, f);
384
+ }
385
+
386
+ /**
387
+ * @tsplus static fncts.io.SinkOps fromChannel
388
+ * @tsplus static fncts.io.SinkOps __call
389
+ */
390
+ export function fromChannel<R, E, In, L, Z>(
391
+ channel: Channel<R, never, Conc<In>, unknown, E, Conc<L>, Z>,
392
+ ): Sink<R, E, In, L, Z> {
393
+ return new Sink(channel);
394
+ }
395
+
396
+ /**
397
+ * @tsplus static fncts.io.SinkOps fromPush
398
+ */
399
+ export function fromPush<R, E, In, L, Z, R1>(
400
+ push: IO<Has<Scope> & R, never, (_: Maybe<Conc<In>>) => IO<R1, readonly [Either<E, Z>, Conc<L>], void>>,
401
+ __tsplusTrace?: string,
402
+ ): Sink<R & R1, E, In, L, Z> {
403
+ return new Sink(Channel.unwrapScoped(push.map(fromPushPull)));
404
+ }
405
+
406
+ function fromPushPull<R, E, In, L, Z>(
407
+ push: (_: Maybe<Conc<In>>) => IO<R, readonly [Either<E, Z>, Conc<L>], void>,
408
+ ): Channel<R, never, Conc<In>, unknown, E, Conc<L>, Z> {
409
+ return Channel.readWith(
410
+ (inp: Conc<In>) =>
411
+ Channel.fromIO(push(Just(inp))).matchChannel(
412
+ ([r, leftovers]) =>
413
+ r.match(
414
+ (e) => Channel.writeNow(leftovers) > Channel.failNow(e),
415
+ (z) => Channel.writeNow(leftovers) > Channel.succeedNow(z),
416
+ ),
417
+ () => fromPushPull(push),
418
+ ),
419
+ Channel.failNow,
420
+ () =>
421
+ Channel.fromIO(push(Nothing())).matchChannel(
422
+ ([r, leftovers]) =>
423
+ r.match(
424
+ (e) => Channel.writeNow(leftovers) > Channel.failNow(e),
425
+ (z) => Channel.writeNow(leftovers) > Channel.succeedNow(z),
426
+ ),
427
+ () => Channel.fromIO(IO.halt(new Error("empty sink"))),
428
+ ),
429
+ );
430
+ }
431
+
432
+ /**
433
+ * @tsplus static fncts.io.SinkOps fromQueue
434
+ */
435
+ export function fromQueue<In>(queue: Lazy<Queue.Enqueue<In>>): Sink<unknown, never, In, never, void> {
436
+ return Sink.unwrap(IO.succeed(queue).map((queue) => Sink.foreachChunk((inp) => queue.offerAll(inp))));
437
+ }
438
+
439
+ /**
440
+ * @tsplus static fncts.io.SinkOps fromQueueWithShutdown
441
+ */
442
+ export function fromQueueWithShutdown<In>(queue: Lazy<Queue.Enqueue<In>>): Sink<unknown, never, In, never, void> {
443
+ return Sink.unwrapScoped(
444
+ IO.succeed(queue)
445
+ .acquireRelease((queue) => queue.shutdown)
446
+ .map((queue) => Sink.fromQueue(queue)),
447
+ );
448
+ }
449
+
450
+ /**
451
+ * @tsplus static fncts.io.SinkOps fromHub
452
+ */
453
+ export function fromHub<In>(hub: Lazy<Hub<In>>): Sink<unknown, never, In, never, void> {
454
+ return Sink.fromQueue(hub);
455
+ }
456
+
457
+ /**
458
+ * @tsplus static fncts.io.SinkOps fromHubWithShutdown
459
+ */
460
+ export function fromHubWithShutdown<In>(hub: Lazy<Hub<In>>): Sink<unknown, never, In, never, void> {
461
+ return Sink.fromQueueWithShutdown(hub);
462
+ }
463
+
464
+ /**
465
+ * @tsplus static fncts.io.SinkOps fromIO
466
+ */
467
+ export function fromIO<R, E, Z>(b: Lazy<IO<R, E, Z>>): Sink<R, E, unknown, never, Z> {
468
+ return new Sink(Channel.fromIO(b));
469
+ }
470
+
471
+ /**
472
+ * @tsplus static fncts.io.SinkOps halt
473
+ */
474
+ export function halt(defect: Lazy<unknown>, __tsplusTrace?: string): Sink<unknown, never, unknown, never, never> {
475
+ return Sink.failCause(Cause.halt(defect()));
476
+ }
477
+
478
+ /**
479
+ * @tsplus static fncts.io.SinkOps haltNow
480
+ */
481
+ export function haltNow(defect: unknown, __tsplusTrace?: string): Sink<unknown, never, unknown, never, never> {
482
+ return Sink.failCauseNow(Cause.halt(defect));
483
+ }
484
+
485
+ /**
486
+ * @tsplus static fncts.io.SinkOps head
487
+ */
488
+ export function head<In>(__tsplusTrace?: string): Sink<unknown, never, In, In, Maybe<In>> {
489
+ return Sink.fold(
490
+ Nothing(),
491
+ (elem) => elem.isNothing(),
492
+ (s, inp) =>
493
+ s.match(
494
+ () => Just(inp),
495
+ () => s,
496
+ ),
497
+ );
498
+ }
499
+
500
+ /**
501
+ * @tsplus getter fncts.io.Sink ignoreLeftover
502
+ */
503
+ export function ignoreLeftover<R, E, In, L, Z>(
504
+ self: Sink<R, E, In, L, Z>,
505
+ __tsplusTrace?: string,
506
+ ): Sink<R, E, In, never, Z> {
507
+ return new Sink(self.channel.drain);
508
+ }
509
+
510
+ /**
511
+ * @tsplus static fncts.io.SinkOps last
512
+ */
513
+ export function last<In>(__tsplusTrace?: string): Sink<unknown, never, In, In, Maybe<In>> {
514
+ return Sink.foldLeft(Nothing(), (_, inp) => Just(inp));
515
+ }
516
+
517
+ /**
518
+ * @tsplus static fncts.io.SinkOps leftover
519
+ */
520
+ export function leftover<L>(c: Lazy<Conc<L>>, __tsplusTrace?: string): Sink<unknown, never, unknown, L, void> {
521
+ return new Sink(Channel.write(c));
522
+ }
523
+
524
+ /**
525
+ * @tsplus static fncts.io.SinkOps log
526
+ */
527
+ export function log(message: Lazy<string>, __tsplusTrace?: string): Sink<unknown, never, unknown, never, void> {
528
+ return Sink.fromIO(IO.log(message));
529
+ }
530
+
531
+ /**
532
+ * A sink that collects all of its inputs into a chunk.
533
+ *
534
+ * @tsplus static fncts.io.SinkOps collectAll
535
+ */
536
+ export function makeCollectAll<In>(): Sink<unknown, never, In, never, Conc<In>> {
537
+ return new Sink(collectLoop<In>(Conc.empty()));
538
+ }
539
+
540
+ function collectLoop<A>(state: Conc<A>): Channel<unknown, never, Conc<A>, unknown, never, Conc<never>, Conc<A>> {
541
+ return Channel.readWithCause(
542
+ (inp: Conc<A>) => collectLoop(state.concat(inp)),
543
+ Channel.failCauseNow,
544
+ () => Channel.endNow(state),
545
+ );
546
+ }
547
+
548
+ export function makeCollectAllN<In>(n: Lazy<number>): Sink<unknown, never, In, In, Conc<In>> {
549
+ return Sink.fromIO(IO.succeed(new ConcBuilder<In>())).flatMap((builder) =>
550
+ Sink.foldUntil<In, ConcBuilder<In>>(builder, n, (builder, inp) => builder.append(inp)).map((builder) =>
551
+ builder.result(),
552
+ ),
553
+ );
554
+ }
555
+
51
556
  /**
52
557
  * A sink that executes the provided effectful function for every element fed to it.
53
558
  *
54
559
  * @tsplus static fncts.io.SinkOps foreach
55
560
  */
56
- export function foreach<R, Err, In>(f: (inp: In) => IO<R, Err, any>): Sink<R, Err, In, In, void> {
561
+ export function makeForeach<R, Err, In>(
562
+ f: (inp: In) => IO<R, Err, any>,
563
+ __tsplusTrace?: string,
564
+ ): Sink<R, Err, In, In, void> {
57
565
  return Sink.foreachWhile((inp) => f(inp).as(true));
58
566
  }
59
567
 
568
+ /**
569
+ * @tsplus static fncts.io.SinkOps foreachChunk
570
+ */
571
+ export function makeForeachChunk<R, E, In>(
572
+ f: (inp: Conc<In>) => IO<R, E, void>,
573
+ __tsplusTrace?: string,
574
+ ): Sink<R, E, In, never, void> {
575
+ const process: Channel<R, E, Conc<In>, unknown, E, never, void> = Channel.readWithCause(
576
+ (inp: Conc<In>) => Channel.fromIO(f(inp)) > process,
577
+ Channel.failCauseNow,
578
+ () => Channel.unit,
579
+ );
580
+
581
+ return new Sink(process);
582
+ }
583
+
584
+ /**
585
+ * A sink that executes the provided effectful function for every element fed to it
586
+ * until `f` evaluates to `false`.
587
+ *
588
+ * @tsplus static fncts.io.SinkOps foreachWhile
589
+ */
590
+ export function makeForeachWhile<R, Err, In>(
591
+ f: (_: In) => IO<R, Err, boolean>,
592
+ __tsplusTrace?: string,
593
+ ): Sink<R, Err, In, In, void> {
594
+ const process: Channel<R, Err, Conc<In>, unknown, Err, Conc<In>, void> = Channel.readWithCause(
595
+ (inp: Conc<In>) => foreachWhileLoop(f, inp, 0, inp.length, process),
596
+ Channel.failCauseNow,
597
+ () => Channel.unit,
598
+ );
599
+ return new Sink(process);
600
+ }
601
+
60
602
  function foreachWhileLoop<R, Err, In>(
61
603
  f: (_: In) => IO<R, Err, boolean>,
62
604
  chunk: Conc<In>,
@@ -73,16 +615,805 @@ function foreachWhileLoop<R, Err, In>(
73
615
  }
74
616
 
75
617
  /**
76
- * A sink that executes the provided effectful function for every element fed to it
77
- * until `f` evaluates to `false`.
618
+ * @tsplus static fncts.io.SinkOps foreachChunkWhile
619
+ */
620
+ export function makeForeachChunkWhile<R, E, In>(
621
+ f: (chunk: Conc<In>) => IO<R, E, boolean>,
622
+ __tsplusTrace?: string,
623
+ ): Sink<R, E, In, In, void> {
624
+ const reader: Channel<R, E, Conc<In>, unknown, E, never, void> = Channel.readWith(
625
+ (inp: Conc<In>) => Channel.fromIO(f(inp)).flatMap((cont) => (cont ? reader : Channel.unit)),
626
+ Channel.failNow,
627
+ () => Channel.unit,
628
+ );
629
+
630
+ return new Sink(reader);
631
+ }
632
+
633
+ /**
634
+ * @tsplus static fncts.io.SinkOps fold
635
+ */
636
+ export function makeFold<In, S>(
637
+ z: Lazy<S>,
638
+ contFn: Predicate<S>,
639
+ f: (s: S, inp: In) => S,
640
+ __tsplusTrace?: string,
641
+ ): Sink<unknown, never, In, In, S> {
642
+ return Sink.defer(new Sink(foldReader(z(), contFn, f)));
643
+ }
644
+
645
+ /**
646
+ * @tsplus tailRec
647
+ */
648
+ function foldChunkSplit<S, In>(
649
+ contFn: (s: S) => boolean,
650
+ f: (s: S, inp: In) => S,
651
+ s: S,
652
+ chunk: Conc<In>,
653
+ idx: number,
654
+ len: number,
655
+ ): readonly [S, Conc<In>] {
656
+ if (idx === len) {
657
+ return [s, Conc.empty()];
658
+ } else {
659
+ const s1 = f(s, chunk[idx]);
660
+ if (contFn(s1)) {
661
+ return foldChunkSplit(contFn, f, s1, chunk, idx + 1, len);
662
+ } else {
663
+ return [s1, chunk.drop(idx + 1)];
664
+ }
665
+ }
666
+ }
667
+
668
+ function foldReader<S, In>(
669
+ s: S,
670
+ contFn: Predicate<S>,
671
+ f: (s: S, inp: In) => S,
672
+ ): Channel<unknown, never, Conc<In>, any, never, Conc<In>, S> {
673
+ if (!contFn(s)) {
674
+ return Channel.succeedNow(s);
675
+ } else {
676
+ return Channel.readWith(
677
+ (inp: Conc<In>) => {
678
+ const [nextS, leftovers] = foldChunkSplit(contFn, f, s, inp, 0, inp.length);
679
+ if (leftovers.isNonEmpty) {
680
+ return Channel.writeNow(leftovers).as(nextS);
681
+ } else {
682
+ return foldReader(nextS, contFn, f);
683
+ }
684
+ },
685
+ (_: never) => Channel.failNow(_),
686
+ (_: S) => Channel.succeedNow(_),
687
+ );
688
+ }
689
+ }
690
+
691
+ /**
692
+ * @tsplus static fncts.io.SinkOps foldUntil
693
+ */
694
+ export function makeFoldUntil<In, S>(
695
+ z: Lazy<S>,
696
+ max: Lazy<number>,
697
+ f: (s: S, inp: In) => S,
698
+ __tsplusTrace?: string,
699
+ ): Sink<unknown, never, In, In, S> {
700
+ return Sink.unwrap(
701
+ IO.succeed(max).map((max) =>
702
+ Sink.fold<In, readonly [S, number]>(
703
+ [z(), 0],
704
+ ([_, n]) => n < max,
705
+ ([o, count], i) => [f(o, i), count + 1],
706
+ ).map(([s]) => s),
707
+ ),
708
+ );
709
+ }
710
+
711
+ /**
712
+ * @tsplus static fncts.io.SinkOps foldChunks
713
+ */
714
+ export function makeFoldChunks<In, S>(
715
+ z: Lazy<S>,
716
+ contFn: Predicate<S>,
717
+ f: (s: S, inp: Conc<In>) => S,
718
+ ): Sink<unknown, never, In, never, S> {
719
+ return Sink.defer(new Sink(foldChunksReader(z(), contFn, f)));
720
+ }
721
+
722
+ function foldChunksReader<In, S>(
723
+ s: S,
724
+ contFn: Predicate<S>,
725
+ f: (s: S, inp: Conc<In>) => S,
726
+ ): Channel<unknown, never, Conc<In>, unknown, never, never, S> {
727
+ if (!contFn(s)) {
728
+ return Channel.succeedNow(s);
729
+ } else {
730
+ return Channel.readWith(
731
+ (inp: Conc<In>) => {
732
+ const nextS = f(s, inp);
733
+ return foldChunksReader(nextS, contFn, f);
734
+ },
735
+ (err: never) => Channel.failNow(err),
736
+ (_: any) => Channel.succeedNow(_),
737
+ );
738
+ }
739
+ }
740
+
741
+ /**
742
+ * @tsplus static fncts.io.SinkOps foldChunksIO
743
+ */
744
+ export function makeFoldChunksIO<Env, Err, In, S>(
745
+ z: Lazy<S>,
746
+ contFn: Predicate<S>,
747
+ f: (s: S, inp: Conc<In>) => IO<Env, Err, S>,
748
+ ): Sink<Env, Err, In, In, S> {
749
+ return Sink.defer(new Sink(foldChunksIOReader(z(), contFn, f)));
750
+ }
751
+
752
+ function foldChunksIOReader<Env, Err, In, S>(
753
+ s: S,
754
+ contFn: Predicate<S>,
755
+ f: (s: S, inp: Conc<In>) => IO<Env, Err, S>,
756
+ ): Channel<Env, Err, Conc<In>, unknown, Err, never, S> {
757
+ if (!contFn(s)) {
758
+ return Channel.succeedNow(s);
759
+ } else {
760
+ return Channel.readWith(
761
+ (inp: Conc<In>) => Channel.fromIO(f(s, inp)).flatMap((s) => foldChunksIOReader(s, contFn, f)),
762
+ (err: Err) => Channel.failNow(err),
763
+ (_: any) => Channel.succeedNow(_),
764
+ );
765
+ }
766
+ }
767
+
768
+ /**
769
+ * @tsplus static fncts.io.SinkOps foldLeft
770
+ */
771
+ export function makeFoldLeft<In, S>(z: Lazy<S>, f: (s: S, inp: In) => S): Sink<unknown, never, In, never, S> {
772
+ return Sink.fold(z, () => true, f).ignoreLeftover;
773
+ }
774
+
775
+ /**
776
+ * @tsplus static fncts.io.SinkOps foldLeftChunks
777
+ */
778
+ export function makeFoldLeftChunks<In, S>(
779
+ z: Lazy<S>,
780
+ f: (s: S, inp: Conc<In>) => S,
781
+ ): Sink<unknown, never, In, never, S> {
782
+ return Sink.foldChunks(z, () => true, f).ignoreLeftover;
783
+ }
784
+
785
+ /**
786
+ * @tsplus static fncts.io.SinkOps foldLeftChunksIO
787
+ */
788
+ export function makeFoldLeftChunksIO<R, E, In, S>(
789
+ z: Lazy<S>,
790
+ f: (s: S, inp: Conc<In>) => IO<R, E, S>,
791
+ ): Sink<R, E, In, In, S> {
792
+ return Sink.foldChunksIO(z, () => true, f);
793
+ }
794
+
795
+ /**
796
+ * @tsplus static fncts.io.SinkOps foldLeftIO
797
+ */
798
+ export function makeFoldLeftIO<R, E, In, S>(z: Lazy<S>, f: (s: S, inp: In) => IO<R, E, S>): Sink<R, E, In, In, S> {
799
+ return Sink.foldIO(z, () => true, f);
800
+ }
801
+
802
+ /**
803
+ * @tsplus static fncts.io.SinkOps foldIO
804
+ */
805
+ export function makeFoldIO<R, E, In, S>(
806
+ z: Lazy<S>,
807
+ contFn: Predicate<S>,
808
+ f: (s: S, inp: In) => IO<R, E, S>,
809
+ ): Sink<R, E, In, In, S> {
810
+ return Sink.defer(new Sink(foldIOReader(z(), contFn, f)));
811
+ }
812
+
813
+ function foldChunkSplitIO<R, E, In, S>(
814
+ s: S,
815
+ contFn: (s: S) => boolean,
816
+ f: (s: S, inp: In) => IO<R, E, S>,
817
+ chunk: Conc<In>,
818
+ idx: number,
819
+ len: number,
820
+ ): IO<R, E, readonly [S, Maybe<Conc<In>>]> {
821
+ if (idx === len) {
822
+ return IO.succeedNow([s, Nothing()]);
823
+ } else {
824
+ return f(s, chunk[idx]).flatMap((s1) => {
825
+ if (contFn(s1)) {
826
+ return foldChunkSplitIO(s1, contFn, f, chunk, idx + 1, len);
827
+ } else {
828
+ return IO.succeedNow([s1, Just(chunk.drop(idx + 1))]);
829
+ }
830
+ });
831
+ }
832
+ }
833
+
834
+ function foldIOReader<R, E, In, S>(
835
+ s: S,
836
+ contFn: (s: S) => boolean,
837
+ f: (s: S, inp: In) => IO<R, E, S>,
838
+ ): Channel<R, E, Conc<In>, unknown, E, Conc<In>, S> {
839
+ if (!contFn(s)) {
840
+ return Channel.succeedNow(s);
841
+ } else {
842
+ return Channel.readWith(
843
+ (inp: Conc<In>) =>
844
+ Channel.fromIO(foldChunkSplitIO(s, contFn, f, inp, 0, inp.length)).flatMap(([nextS, leftovers]) =>
845
+ leftovers.match(
846
+ () => foldIOReader(nextS, contFn, f),
847
+ (l) => Channel.writeNow(l).as(nextS),
848
+ ),
849
+ ),
850
+ (err: E) => Channel.failNow(err),
851
+ (_: any) => Channel.succeedNow(_),
852
+ );
853
+ }
854
+ }
855
+
856
+ /**
857
+ * @tsplus static fncts.io.SinkOps foldUntilIO
858
+ */
859
+ export function makeFoldUntilIO<R, E, In, S>(
860
+ z: Lazy<S>,
861
+ max: Lazy<number>,
862
+ f: (s: S, inp: In) => IO<R, E, S>,
863
+ ): Sink<R, E, In, In, S> {
864
+ return Sink.foldIO<R, E, In, readonly [S, number]>(
865
+ [z(), 0],
866
+ ([_, n]) => n < max(),
867
+ ([o, count], i) => f(o, i).map((s) => [s, count + 1]),
868
+ ).map(([s]) => s);
869
+ }
870
+
871
+ /**
872
+ * @tsplus static fncts.io.SinkOps foldWeightedDecompose
873
+ */
874
+ export function makeFoldWeightedDecompose<In, S>(
875
+ z: Lazy<S>,
876
+ costFn: (s: S, inp: In) => number,
877
+ max: Lazy<number>,
878
+ decompose: (inp: In) => Conc<In>,
879
+ f: (s: S, inp: In) => S,
880
+ ): Sink<unknown, never, In, In, S> {
881
+ return Sink.defer(() => {
882
+ /**
883
+ * @tsplus tailRec
884
+ */
885
+ function fold(
886
+ inp: Conc<In>,
887
+ s: S,
888
+ max: number,
889
+ dirty: boolean,
890
+ cost: number,
891
+ idx: number,
892
+ ): readonly [S, number, boolean, Conc<In>] {
893
+ if (idx === inp.length) {
894
+ return [s, cost, dirty, Conc.empty()];
895
+ } else {
896
+ const elem = inp[idx];
897
+ const total = cost + costFn(s, elem);
898
+
899
+ if (total <= max) {
900
+ return fold(inp, f(s, elem), max, true, total, idx + 1);
901
+ } else {
902
+ const decomposed = decompose(elem);
903
+
904
+ if (decomposed.length <= 1 && !dirty) {
905
+ return [f(s, elem), total, true, inp.drop(idx + 1)];
906
+ } else if (decomposed.length <= 1 && dirty) {
907
+ return [s, cost, dirty, inp.drop(idx)];
908
+ } else {
909
+ return fold(decomposed.concat(inp.drop(idx + 1)), s, max, dirty, cost, 0);
910
+ }
911
+ }
912
+ }
913
+ }
914
+ function go(
915
+ s: S,
916
+ cost: number,
917
+ dirty: boolean,
918
+ max: number,
919
+ ): Channel<unknown, never, Conc<In>, unknown, never, Conc<In>, S> {
920
+ return Channel.readWith(
921
+ (inp: Conc<In>) => {
922
+ const [nextS, nextCost, nextDirty, leftovers] = fold(inp, s, max, dirty, cost, 0);
923
+
924
+ if (leftovers.isNonEmpty) {
925
+ return Channel.writeNow(leftovers) > Channel.succeedNow(nextS);
926
+ } else if (cost > max) {
927
+ return Channel.succeedNow(nextS);
928
+ } else {
929
+ return go(nextS, nextCost, nextDirty, max);
930
+ }
931
+ },
932
+ (err: never) => Channel.failNow(err),
933
+ (_: any) => Channel.succeedNow(s),
934
+ );
935
+ }
936
+
937
+ return new Sink(go(z(), 0, false, max()));
938
+ });
939
+ }
940
+
941
+ /**
942
+ * @tsplus static fncts.io.SinkOps foldWeightedDecomposeIO
943
+ */
944
+ export function makeFoldWeightedDecomposeIO<R, E, In, S, R1, E1, R2, E2>(
945
+ z: Lazy<S>,
946
+ costFn: (s: S, inp: In) => IO<R1, E1, number>,
947
+ max: Lazy<number>,
948
+ decompose: (inp: In) => IO<R2, E2, Conc<In>>,
949
+ f: (s: S, inp: In) => IO<R, E, S>,
950
+ ): Sink<R & R1 & R2, E | E1 | E2, In, In, S> {
951
+ return Sink.defer(() => {
952
+ function fold(
953
+ inp: Conc<In>,
954
+ s: S,
955
+ max: number,
956
+ dirty: boolean,
957
+ cost: number,
958
+ idx: number,
959
+ ): IO<R & R1 & R2, E | E1 | E2, readonly [S, number, boolean, Conc<In>]> {
960
+ if (idx === inp.length) {
961
+ return IO.succeedNow([s, cost, dirty, Conc.empty()]);
962
+ } else {
963
+ const elem = inp[idx];
964
+ return costFn(s, elem)
965
+ .map((_) => cost + _)
966
+ .flatMap((total) => {
967
+ if (total <= max) {
968
+ return f(s, elem).flatMap((s) => fold(inp, s, max, true, total, idx + 1));
969
+ } else {
970
+ return decompose(elem).flatMap((decomposed) => {
971
+ if (decomposed.length <= 1 && !dirty) {
972
+ return f(s, elem).map((s) => [s, total, true, inp.drop(idx + 1)]);
973
+ } else if (decomposed.length <= 1 && dirty) {
974
+ return IO.succeedNow([s, cost, dirty, inp.drop(idx)]);
975
+ } else {
976
+ return fold(decomposed.concat(inp.drop(idx + 1)), s, max, dirty, cost, 0);
977
+ }
978
+ });
979
+ }
980
+ });
981
+ }
982
+ }
983
+ function go(
984
+ s: S,
985
+ cost: number,
986
+ dirty: boolean,
987
+ max: number,
988
+ ): Channel<R & R1 & R2, E | E1 | E2, Conc<In>, unknown, E | E1 | E2, Conc<In>, S> {
989
+ return Channel.readWith(
990
+ (inp: Conc<In>) =>
991
+ Channel.fromIO(fold(inp, s, max, dirty, cost, 0)).flatMap(([nextS, nextCost, nextDirty, leftovers]) => {
992
+ if (leftovers.isNonEmpty) {
993
+ return Channel.writeNow(leftovers) > Channel.succeedNow(nextS);
994
+ } else if (cost > max) {
995
+ return Channel.succeedNow(nextS);
996
+ } else {
997
+ return go(nextS, nextCost, nextDirty, max);
998
+ }
999
+ }),
1000
+ (err: E | E1 | E2) => Channel.failNow(err),
1001
+ (_: any) => Channel.succeedNow(s),
1002
+ );
1003
+ }
1004
+
1005
+ return new Sink(go(z(), 0, false, max()));
1006
+ });
1007
+ }
1008
+
1009
+ /**
1010
+ * @tsplus static fncts.io.SinkOps foldWeighted
1011
+ */
1012
+ export function makeFoldWeighted<In, S>(
1013
+ z: Lazy<S>,
1014
+ costFn: (s: S, inp: In) => number,
1015
+ max: Lazy<number>,
1016
+ f: (s: S, inp: In) => S,
1017
+ ): Sink<unknown, never, In, In, S> {
1018
+ return Sink.foldWeightedDecompose(z, costFn, max, Conc.single, f);
1019
+ }
1020
+
1021
+ /**
1022
+ * @tsplus static fncts.io.SinkOps foldWeightedIO
1023
+ */
1024
+ export function makeFoldWeightedIO<R, E, In, S, R1, E1>(
1025
+ z: Lazy<S>,
1026
+ costFn: (s: S, inp: In) => IO<R, E, number>,
1027
+ max: Lazy<number>,
1028
+ f: (s: S, inp: In) => IO<R1, E1, S>,
1029
+ ): Sink<R & R1, E | E1, In, In, S> {
1030
+ return Sink.foldWeightedDecomposeIO(z, costFn, max, (inp) => IO.succeedNow(Conc.single(inp)), f);
1031
+ }
1032
+
1033
+ /**
1034
+ * Transforms this sink's result.
78
1035
  *
79
- * @tsplus static fncts.io.SinkOps foreachWhile
1036
+ * @tsplus fluent fncts.io.Sink map
80
1037
  */
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),
1038
+ export function map_<R, E, In, L, Z, Z1>(
1039
+ self: Sink<R, E, In, L, Z>,
1040
+ f: (z: Z) => Z1,
1041
+ __tsplusTrace?: string,
1042
+ ): Sink<R, E, In, L, Z1> {
1043
+ return new Sink(self.channel.map(f));
1044
+ }
1045
+
1046
+ /**
1047
+ * Transforms the errors emitted by this sink using `f`.
1048
+ *
1049
+ * @tsplus fluent fncts.io.Sink mapError
1050
+ */
1051
+ export function mapError_<R, E, In, L, Z, E1>(
1052
+ self: Sink<R, E, In, L, Z>,
1053
+ f: (e: E) => E1,
1054
+ __tsplusTrace?: string,
1055
+ ): Sink<R, E1, In, L, Z> {
1056
+ return new Sink(self.channel.mapError(f));
1057
+ }
1058
+
1059
+ /**
1060
+ * Effectfully transforms this sink's result.
1061
+ *
1062
+ * @tsplus fluent fncts.io.Sink mapIO
1063
+ */
1064
+ export function mapIO_<R, E, In, L, Z, R1, E1, Z1>(
1065
+ self: Sink<R, E, In, L, Z>,
1066
+ f: (z: Z) => IO<R1, E1, Z1>,
1067
+ __tsplusTrace?: string,
1068
+ ): Sink<R & R1, E | E1, In, L, Z1> {
1069
+ return new Sink(self.channel.mapIO(f));
1070
+ }
1071
+
1072
+ /**
1073
+ * Runs this sink until it yields a result, then uses that result to create
1074
+ * another sink from the provided function which will continue to run until it
1075
+ * yields a result.
1076
+ *
1077
+ * This function essentially runs sinks in sequence.
1078
+ *
1079
+ * @tsplus fluent fncts.io.Sink matchSink
1080
+ */
1081
+ export function matchSink_<R, E, In, L, Z, R1, E1, In1 extends In, L1, Z1, R2, E2, In2 extends In, L2, Z2>(
1082
+ self: Sink<R, E, In, L, Z>,
1083
+ onFailure: (e: E) => Sink<R1, E1, In1, L1, Z1>,
1084
+ onSuccess: (z: Z) => Sink<R2, E2, In2, L2, Z2>,
1085
+ __tsplusTrace?: string,
1086
+ ): Sink<R & R1 & R2, E1 | E2, In1 & In2, L | L1 | L2, Z1 | Z2> {
1087
+ return new Sink<R & R1 & R2, E1 | E2, In1 & In2, L | L1 | L2, Z1 | Z2>(
1088
+ self.channel.doneCollect.matchChannel(
1089
+ (e) => onFailure(e).channel,
1090
+ ([leftovers, z]) =>
1091
+ Channel.defer(() => {
1092
+ const leftoversRef = new AtomicReference(leftovers.filter((c) => c.isNonEmpty));
1093
+ const refReader = Channel.succeed(leftoversRef.getAndSet(Conc.empty())).flatMap((chunk) =>
1094
+ Channel.writeChunk(chunk as unknown as Conc<Conc<In1 & In2>>),
1095
+ );
1096
+ const passthrough = Channel.id<never, Conc<In1 & In2>, unknown>();
1097
+ const continuationSink = (refReader > passthrough).pipeTo(onSuccess(z).channel);
1098
+ return continuationSink.doneCollect.flatMap(
1099
+ ([newLeftovers, z1]) =>
1100
+ Channel.succeed(leftoversRef.get).flatMap(Channel.writeChunk) > Channel.writeChunk(newLeftovers).as(z1),
1101
+ );
1102
+ }),
1103
+ ),
1104
+ );
1105
+ }
1106
+
1107
+ /**
1108
+ * @tsplus fluent fncts.io.Sink orElse
1109
+ */
1110
+ export function orElse<R, E, In, L, Z, R1, E1, In1, L1, Z1>(
1111
+ self: Sink<R, E, In, L, Z>,
1112
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
1113
+ __tsplusTrace?: string,
1114
+ ): Sink<R & R1, E | E1, In & In1, L | L1, Z | Z1> {
1115
+ return Sink.defer(new Sink<R & R1, E | E1, In & In1, L | L1, Z | Z1>(self.channel.orElse(that().channel)));
1116
+ }
1117
+
1118
+ /**
1119
+ * @tsplus fluent fncts.io.Sink provideEnvironment
1120
+ */
1121
+ export function provideEnvironment<R, E, In, L, Z>(
1122
+ self: Sink<R, E, In, L, Z>,
1123
+ r: Lazy<Environment<R>>,
1124
+ __tsplusTrace?: string,
1125
+ ): Sink<unknown, E, In, L, Z> {
1126
+ return new Sink(self.channel.provideEnvironment(r));
1127
+ }
1128
+
1129
+ /**
1130
+ * @tsplus fluent fncts.io.Sink race
1131
+ */
1132
+ export function race<R, E, In, L, Z, R1, E1, In1, L1, Z1>(
1133
+ self: Sink<R, E, In, L, Z>,
1134
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
1135
+ ): Sink<R & R1, E | E1, In & In1, L | L1, Z | Z1> {
1136
+ return self.raceBoth(that).map((result) => result.value);
1137
+ }
1138
+
1139
+ /**
1140
+ * @tsplus fluent fncts.io.Sink raceBoth
1141
+ */
1142
+ export function raceBoth<R, E, In, L, Z, R1, E1, In1, L1, Z1>(
1143
+ self: Sink<R, E, In, L, Z>,
1144
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
1145
+ capacity: Lazy<number> = () => 16,
1146
+ ): Sink<R & R1, E | E1, In & In1, L | L1, Either<Z, Z1>> {
1147
+ return self.raceWith(
1148
+ that,
1149
+ (selfDone) => MergeDecision.Done(IO.fromExitNow(selfDone).map(Either.left)),
1150
+ (thatDone) => MergeDecision.Done(IO.fromExitNow(thatDone).map(Either.right)),
1151
+ capacity,
1152
+ );
1153
+ }
1154
+
1155
+ /**
1156
+ * @tsplus fluent fncts.io.Sink raceWith
1157
+ */
1158
+ export function raceWith<R, E, In, L, Z, R1, E1, In1, L1, Z1, R2, E2, Z2, R3, E3, Z3>(
1159
+ self: Sink<R, E, In, L, Z>,
1160
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
1161
+ leftDone: (exit: Exit<E, Z>) => MergeDecision<R1, E1, Z1, E2, Z2>,
1162
+ rightDone: (exit: Exit<E1, Z1>) => MergeDecision<R, E, Z, E3, Z3>,
1163
+ capacity: Lazy<number> = () => 16,
1164
+ __tsplusTrace?: string,
1165
+ ): Sink<R & R1 & R2 & R3, E2 | E3, In & In1, L | L1, Z2 | Z3> {
1166
+ const scoped = IO.defer(() => {
1167
+ const that0 = that();
1168
+ const capacity0 = capacity();
1169
+ return Do((_) => {
1170
+ const hub = _(Hub.makeBounded<Either<Exit<never, any>, Conc<In & In1>>>(capacity()));
1171
+ const c1 = _(Channel.fromHubScoped(hub));
1172
+ const c2 = _(Channel.fromHubScoped(hub));
1173
+ const reader = Channel.toHub(hub);
1174
+ const writer = c1.pipeTo(self.channel).mergeWith(c2.pipeTo(that0.channel), leftDone, rightDone);
1175
+ const channel = reader.mergeWith(
1176
+ writer,
1177
+ () => MergeDecision.Await(IO.fromExitNow),
1178
+ (done) => MergeDecision.Done(IO.fromExitNow(done)),
1179
+ );
1180
+ return new Sink<R & R1 & R2 & R3, E2 | E3, In & In1, L | L1, Z2 | Z3>(channel);
1181
+ });
1182
+ });
1183
+ return Sink.unwrapScoped(scoped);
1184
+ }
1185
+
1186
+ /**
1187
+ * @tsplus static fncts.io.SinkOps service
1188
+ */
1189
+ export function service<S>(/** @tsplus auto */ tag: Tag<S>): Sink<Has<S>, never, unknown, never, S> {
1190
+ return Sink.serviceWith(Function.identity);
1191
+ }
1192
+
1193
+ /**
1194
+ * @tsplus static fncts.io.SinkOps serviceWith
1195
+ */
1196
+ export function serviceWith<S, Z>(
1197
+ f: (service: S) => Z,
1198
+ /** @tsplus auto */ tag: Tag<S>,
1199
+ ): Sink<Has<S>, never, unknown, never, Z> {
1200
+ return Sink.fromIO(IO.serviceWith(f, tag));
1201
+ }
1202
+
1203
+ /**
1204
+ * @tsplus static fncts.io.SinkOps serviceWithIO
1205
+ */
1206
+ export function serviceWithIO<S, R, E, Z>(
1207
+ f: (service: S) => IO<R, E, Z>,
1208
+ /** @tsplus auto */ tag: Tag<S>,
1209
+ ): Sink<Has<S> & R, E, unknown, never, Z> {
1210
+ return Sink.fromIO(IO.serviceWithIO(f, tag));
1211
+ }
1212
+
1213
+ /**
1214
+ * @tsplus static fncts.io.SinkOps serviceWithSink
1215
+ */
1216
+ export function serviceWithSink<S, R, E, In, L, Z>(
1217
+ f: (service: S) => Sink<R, E, In, L, Z>,
1218
+ /** @tsplus auto */ tag: Tag<S>,
1219
+ ): Sink<Has<S> & R, E, In, L, Z> {
1220
+ return new Sink(
1221
+ Channel.unwrap(
1222
+ IO.serviceWith(
1223
+ f.compose((s) => s.channel),
1224
+ tag,
1225
+ ),
1226
+ ),
1227
+ );
1228
+ }
1229
+
1230
+ /**
1231
+ * @tsplus fluent fncts.io.Sink splitWhere
1232
+ */
1233
+ export function splitWhere<R, E, In, L extends In, Z>(
1234
+ self: Sink<R, E, In, L, Z>,
1235
+ p: Predicate<In>,
1236
+ ): Sink<R, E, In, In, Z> {
1237
+ return new Sink(
1238
+ Channel.fromIO(Ref.make<Conc<In>>(Conc.empty())).flatMap((ref) =>
1239
+ splitter<R, E, In>(p, false, ref)
1240
+ .pipeToOrFail(self.channel)
1241
+ .collectElements.flatMap(([leftovers, z]) =>
1242
+ Channel.fromIO(ref.get).flatMap(
1243
+ (leftover) => Channel.writeNow(leftover.concat(leftovers.flatten)) > Channel.succeedNow(z),
1244
+ ),
1245
+ ),
1246
+ ),
1247
+ );
1248
+ }
1249
+
1250
+ function splitter<R, E, In>(
1251
+ p: Predicate<In>,
1252
+ written: boolean,
1253
+ leftovers: Ref<Conc<In>>,
1254
+ ): Channel<R, never, Conc<In>, unknown, E, Conc<In>, unknown> {
1255
+ return Channel.readWithCause(
1256
+ (inp) => {
1257
+ if (inp.isEmpty) {
1258
+ return splitter(p, written, leftovers);
1259
+ } else if (written) {
1260
+ const index = inp.findIndex(p);
1261
+ if (index === -1) {
1262
+ return Channel.writeNow(inp) > splitter<R, E, In>(p, true, leftovers);
1263
+ } else {
1264
+ const [left, right] = inp.splitAt(index);
1265
+ return Channel.writeNow(left) > Channel.fromIO(leftovers.set(right));
1266
+ }
1267
+ } else {
1268
+ const index = inp.findIndex(p);
1269
+ if (index === -1) {
1270
+ return Channel.writeNow(inp) > splitter<R, E, In>(p, true, leftovers);
1271
+ } else {
1272
+ const [left, right] = inp.splitAt(Math.max(index, 1));
1273
+ return Channel.writeNow(left) > Channel.fromIO(leftovers.set(right));
1274
+ }
1275
+ }
1276
+ },
84
1277
  Channel.failCauseNow,
85
- () => Channel.unit,
1278
+ Channel.succeedNow,
1279
+ );
1280
+ }
1281
+
1282
+ /**
1283
+ * @tsplus static fncts.io.SinkOps succeed
1284
+ */
1285
+ export function succeed<Z>(z: Lazy<Z>, __tsplusTrace?: string): Sink<unknown, never, unknown, never, Z> {
1286
+ return new Sink(Channel.succeed(z));
1287
+ }
1288
+
1289
+ /**
1290
+ * @tsplus static fncts.io.SinkOps succeedNow
1291
+ */
1292
+ export function succeedNow<Z>(z: Z, __tsplusTrace?: string): Sink<unknown, never, unknown, never, Z> {
1293
+ return new Sink(Channel.succeedNow(z));
1294
+ }
1295
+
1296
+ /**
1297
+ * @tsplus fluent fncts.io.Sink summarized
1298
+ */
1299
+ export function summarized<R, E, In, L, Z, R1, E1, B, C>(
1300
+ self: Sink<R, E, In, L, Z>,
1301
+ summary: Lazy<IO<R1, E1, B>>,
1302
+ f: (b1: B, b2: B) => C,
1303
+ __tsplusTrace?: string,
1304
+ ): Sink<R & R1, E | E1, In, L, readonly [Z, C]> {
1305
+ return new Sink(
1306
+ Channel.unwrap(
1307
+ IO.succeed(summary).map((summary) =>
1308
+ Channel.fromIO(summary).flatMap((start) =>
1309
+ self.channel.flatMap((done) => Channel.fromIO(summary).map((end) => [done, f(start, end)])),
1310
+ ),
1311
+ ),
1312
+ ),
1313
+ );
1314
+ }
1315
+
1316
+ /**
1317
+ * @tsplus getter fncts.io.Sink timed
1318
+ */
1319
+ export function timed<R, E, In, L, Z>(
1320
+ self: Sink<R, E, In, L, Z>,
1321
+ __tsplusTrace?: string,
1322
+ ): Sink<R, E, In, L, readonly [Z, Duration]> {
1323
+ return self.summarized(Clock.currentTime, (start, end) => Duration.fromInterval(start, end));
1324
+ }
1325
+
1326
+ /**
1327
+ * @tsplus static fncts.io.SinkOps unwrap
1328
+ */
1329
+ export function unwrap<R, E, R1, E1, In, L, Z>(
1330
+ io: Lazy<IO<R, E, Sink<R1, E1, In, L, Z>>>,
1331
+ ): Sink<R & R1, E | E1, In, L, Z> {
1332
+ return new Sink(Channel.unwrap(io().map((sink) => sink.channel)));
1333
+ }
1334
+
1335
+ /**
1336
+ * Creates a sink produced from a scoped effect.
1337
+ *
1338
+ * @tsplus static fncts.io.SinkOps unwrapScoped
1339
+ */
1340
+ export function unwrapScoped<R, E, R1, E1, In, L, Z>(
1341
+ scoped: Lazy<IO<Has<Scope> & R, E, Sink<R1, E1, In, L, Z>>>,
1342
+ ): Sink<R & R1, E | E1, In, L, Z> {
1343
+ return new Sink(Channel.unwrapScoped(scoped().map((sink) => sink.channel)));
1344
+ }
1345
+
1346
+ /**
1347
+ * @tsplus fluent fncts.io.Sink zip
1348
+ */
1349
+ export function zip<R, E, In, L, Z, R1, E1, In1 extends In, L1 extends L, Z1>(
1350
+ self: Sink<R, E, In, L, Z>,
1351
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
1352
+ ): Sink<R & R1, E | E1, In & In1, L | L1, readonly [Z, Z1]> {
1353
+ return self.zipWith(that, Function.tuple);
1354
+ }
1355
+
1356
+ /**
1357
+ * @tsplus fluent fncts.io.Sink zipC
1358
+ */
1359
+ export function zipC<R, E, In, L, Z, R1, E1, In1 extends In, L1 extends L, Z1>(
1360
+ self: Sink<R, E, In, L, Z>,
1361
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
1362
+ ): Sink<R & R1, E | E1, In & In1, L | L1, readonly [Z, Z1]> {
1363
+ return self.zipWithC(that, Function.tuple);
1364
+ }
1365
+
1366
+ /**
1367
+ * Feeds inputs to this sink until it yields a result, then switches over to
1368
+ * the provided sink until it yields a result, finally combining the two
1369
+ * results with `f`.
1370
+ *
1371
+ * @tsplus fluent fncts.io.Sink zipWith
1372
+ */
1373
+ export function zipWith<R, E, In, L, Z, R1, E1, In1 extends In, L1 extends L, Z1, Z2>(
1374
+ self: Lazy<Sink<R, E, In, L, Z>>,
1375
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
1376
+ f: (z: Z, z1: Z1) => Z2,
1377
+ ): Sink<R & R1, E | E1, In & In1, L | L1, Z2> {
1378
+ return Sink.defer(self().flatMap((z) => that().map((z1) => f(z, z1))));
1379
+ }
1380
+
1381
+ /**
1382
+ * Runs both sinks in parallel on the input and combines the results using the
1383
+ * provided function.
1384
+ *
1385
+ * @tsplus fluent fncts.io.Sink zipWithC
1386
+ */
1387
+ export function zipWithC<R, E, In, L, Z, R1, E1, In1, L1, Z1, Z2>(
1388
+ self: Lazy<Sink<R, E, In, L, Z>>,
1389
+ that: Lazy<Sink<R1, E1, In1, L1, Z1>>,
1390
+ f: (z: Z, z1: Z1) => Z2,
1391
+ ): Sink<R & R1, E | E1, In & In1, L | L1, Z2> {
1392
+ return Sink.defer(
1393
+ self().raceWith(
1394
+ that(),
1395
+ (exit) =>
1396
+ exit.match(
1397
+ (err) => MergeDecision.Done(IO.failCauseNow(err)),
1398
+ (lz) =>
1399
+ MergeDecision.Await((exit) =>
1400
+ exit.match(
1401
+ (cause) => IO.failCauseNow(cause),
1402
+ (rz) => IO.succeedNow(f(lz, rz)),
1403
+ ),
1404
+ ),
1405
+ ),
1406
+ (exit) =>
1407
+ exit.match(
1408
+ (err) => MergeDecision.Done(IO.failCauseNow(err)),
1409
+ (rz) =>
1410
+ MergeDecision.Await((exit) =>
1411
+ exit.match(
1412
+ (cause) => IO.failCauseNow(cause),
1413
+ (lz) => IO.succeedNow(f(lz, rz)),
1414
+ ),
1415
+ ),
1416
+ ),
1417
+ ),
86
1418
  );
87
- return new Sink(process);
88
1419
  }