@fncts/io 0.0.35 → 0.0.37

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. package/Fiber/constructors.d.ts +5 -0
  2. package/Push/api.d.ts +70 -41
  3. package/Push/definition.d.ts +11 -13
  4. package/Push/internal.d.ts +7 -11
  5. package/RefSubject/Atomic.d.ts +8 -11
  6. package/RefSubject/Synchronized/definition.d.ts +4 -6
  7. package/RefSubject/api.d.ts +0 -1
  8. package/RefSubject/definition.d.ts +6 -8
  9. package/STM/definition.d.ts +17 -0
  10. package/Sink/api.d.ts +6 -6
  11. package/Subject/Atomic.d.ts +4 -6
  12. package/Subject/definition.d.ts +2 -2
  13. package/_cjs/Fiber/constructors.cjs +10 -2
  14. package/_cjs/Fiber/constructors.cjs.map +1 -1
  15. package/_cjs/IO/definition.cjs.map +1 -1
  16. package/_cjs/Layer/api.cjs.map +1 -1
  17. package/_cjs/Push/api.cjs +206 -175
  18. package/_cjs/Push/api.cjs.map +1 -1
  19. package/_cjs/Push/definition.cjs +12 -13
  20. package/_cjs/Push/definition.cjs.map +1 -1
  21. package/_cjs/Push/internal.cjs +37 -29
  22. package/_cjs/Push/internal.cjs.map +1 -1
  23. package/_cjs/RefSubject/Atomic.cjs +15 -19
  24. package/_cjs/RefSubject/Atomic.cjs.map +1 -1
  25. package/_cjs/RefSubject/Synchronized/definition.cjs +9 -10
  26. package/_cjs/RefSubject/Synchronized/definition.cjs.map +1 -1
  27. package/_cjs/RefSubject/api.cjs +5 -6
  28. package/_cjs/RefSubject/api.cjs.map +1 -1
  29. package/_cjs/RefSubject/definition.cjs.map +1 -1
  30. package/_cjs/STM/api.cjs +2 -2
  31. package/_cjs/STM/api.cjs.map +1 -1
  32. package/_cjs/STM/definition.cjs.map +1 -1
  33. package/_cjs/Sink/api.cjs +13 -13
  34. package/_cjs/Sink/api.cjs.map +1 -1
  35. package/_cjs/Subject/Atomic.cjs +4 -5
  36. package/_cjs/Subject/Atomic.cjs.map +1 -1
  37. package/_mjs/Fiber/constructors.mjs +7 -1
  38. package/_mjs/Fiber/constructors.mjs.map +1 -1
  39. package/_mjs/IO/definition.mjs.map +1 -1
  40. package/_mjs/Layer/api.mjs.map +1 -1
  41. package/_mjs/Push/api.mjs +194 -173
  42. package/_mjs/Push/api.mjs.map +1 -1
  43. package/_mjs/Push/definition.mjs +9 -10
  44. package/_mjs/Push/definition.mjs.map +1 -1
  45. package/_mjs/Push/internal.mjs +33 -22
  46. package/_mjs/Push/internal.mjs.map +1 -1
  47. package/_mjs/RefSubject/Atomic.mjs +15 -19
  48. package/_mjs/RefSubject/Atomic.mjs.map +1 -1
  49. package/_mjs/RefSubject/Synchronized/definition.mjs +9 -10
  50. package/_mjs/RefSubject/Synchronized/definition.mjs.map +1 -1
  51. package/_mjs/RefSubject/api.mjs +6 -7
  52. package/_mjs/RefSubject/api.mjs.map +1 -1
  53. package/_mjs/RefSubject/definition.mjs.map +1 -1
  54. package/_mjs/STM/api.mjs +2 -2
  55. package/_mjs/STM/api.mjs.map +1 -1
  56. package/_mjs/STM/definition.mjs.map +1 -1
  57. package/_mjs/Sink/api.mjs +10 -10
  58. package/_mjs/Sink/api.mjs.map +1 -1
  59. package/_mjs/Subject/Atomic.mjs +4 -5
  60. package/_mjs/Subject/Atomic.mjs.map +1 -1
  61. package/_src/Fiber/constructors.ts +5 -0
  62. package/_src/IO/definition.ts +0 -1
  63. package/_src/Layer/api.ts +0 -1
  64. package/_src/Push/api.ts +270 -335
  65. package/_src/Push/definition.ts +13 -17
  66. package/_src/Push/internal.ts +63 -31
  67. package/_src/RefSubject/Atomic.ts +16 -22
  68. package/_src/RefSubject/Synchronized/definition.ts +6 -9
  69. package/_src/RefSubject/api.ts +9 -12
  70. package/_src/RefSubject/definition.ts +6 -8
  71. package/_src/STM/api.ts +0 -5
  72. package/_src/STM/definition.ts +6 -0
  73. package/_src/Sink/api.ts +9 -9
  74. package/_src/Subject/Atomic.ts +6 -8
  75. package/_src/Subject/definition.ts +2 -2
  76. package/package.json +2 -2
package/_src/Push/api.ts CHANGED
@@ -1,9 +1,8 @@
1
- import type { _A, _E, _R } from "@fncts/base/types";
2
-
3
1
  import { AtomicReference } from "@fncts/base/internal/AtomicReference";
2
+ import { IO } from "@fncts/io/IO";
3
+ import { withExhaust, withSwitch, withUnboundedConcurrency } from "@fncts/io/Push/internal";
4
4
 
5
- import { Emitter, Push, PushTypeId, PushVariance } from "./definition.js";
6
- import { earlyExit, onEarlyExit } from "./internal.js";
5
+ import { Push, PushTypeId, PushVariance, Sink } from "./definition.js";
7
6
 
8
7
  /**
9
8
  * @tsplus pipeable fncts.io.Push as
@@ -14,45 +13,44 @@ export function as<B>(b: Lazy<B>) {
14
13
  };
15
14
  }
16
15
 
17
- interface UnsafeEmitter<E, A> {
18
- emit: (value: A) => void;
19
- failCause: (cause: Cause<E>) => void;
20
- end: () => void;
16
+ interface UnsafeSink<E, A> {
17
+ event: (value: A) => void;
18
+ error: (cause: Cause<E>) => void;
21
19
  }
22
20
 
23
21
  /**
24
22
  * @tsplus static fncts.io.PushOps asyncInterrupt
25
23
  */
26
24
  export function asyncInterrupt<R, E, A>(
27
- make: (emitter: UnsafeEmitter<E, A>) => Either<IO<R, never, void>, Push<R, E, A>>,
25
+ make: (emitter: UnsafeSink<E, A>) => Either<IO<R, never, void>, Push<R, E, A>>,
28
26
  ): Push<R, E, A> {
29
- return Push<R, E, A>(<R1>(emitter: Emitter<R | R1, E, A>) =>
30
- Do() => {
31
- const future = Δ(Future.make<never, void>());
32
- const scope = Δ(IO.scope);
33
- const runtime = Δ(IO.runtime<R | R1 | R1>());
34
- const unsafeEmitter: UnsafeEmitter<E, A> = {
35
- emit: (value) => runtime.unsafeRunOrFork(emitter.emit(value).forkIn(scope)),
36
- failCause: (cause) => runtime.unsafeRunOrFork(emitter.failCause(cause).fulfill(future).forkIn(scope)),
37
- end: () => runtime.unsafeRunOrFork(emitter.end > future.succeed(undefined)),
38
- };
39
- const eitherPush = Δ(IO(make(unsafeEmitter)));
40
- Δ(
41
- eitherPush.match(
42
- (canceller) => future.await.onInterrupt(canceller),
43
- (push) => push.run(emitter),
44
- ),
45
- );
46
- }),
27
+ return Push<R, E, A>(
28
+ <R1>(sink: Sink<R | R1, E, A>) =>
29
+ Do((Δ) => {
30
+ const future = Δ(Future.make<never, void>());
31
+ const scope = Δ(IO.scope);
32
+ const runtime = Δ(IO.runtime<R | R1>());
33
+ const unsafeSink: UnsafeSink<E, A> = {
34
+ event: (value) => runtime.unsafeRunOrFork(sink.event(value).forkIn(scope)),
35
+ error: (cause) => runtime.unsafeRunOrFork(sink.error(cause).fulfill(future).forkIn(scope)),
36
+ };
37
+ const eitherPush = Δ(IO(make(unsafeSink)));
38
+ Δ(
39
+ eitherPush.match(
40
+ (canceller) => future.await.onInterrupt(canceller),
41
+ (push) => push.run(sink),
42
+ ),
43
+ );
44
+ }).scoped,
47
45
  );
48
46
  }
49
47
 
50
48
  /**
51
49
  * @tsplus static fncts.io.PushOps async
52
50
  */
53
- export function async<E, A>(make: (emitter: UnsafeEmitter<E, A>) => void): Push<never, E, A> {
54
- return Push.asyncInterrupt((emitter) => {
55
- make(emitter);
51
+ export function async<E, A>(make: (sink: UnsafeSink<E, A>) => void): Push<never, E, A> {
52
+ return Push.asyncInterrupt((sink) => {
53
+ make(sink);
56
54
  return Either.left(IO.unit);
57
55
  });
58
56
  }
@@ -62,31 +60,26 @@ export function async<E, A>(make: (emitter: UnsafeEmitter<E, A>) => void): Push<
62
60
  */
63
61
  export function combineLatest<A extends ReadonlyArray<Push<any, any, any>>>(
64
62
  streams: [...A],
65
- ): Push<_R<A[number]>, _E<A[number]>, { [K in keyof A]: [A[K]] extends [Push<any, any, infer A>] ? A : never }>;
63
+ ): Push<Push.EnvironmentOf<A[number]>, Push.ErrorOf<A[number]>, { [K in keyof A]: Push.ValueOf<A[K]> }>;
66
64
  export function combineLatest<R, E, A>(streams: Iterable<Push<R, E, A>>): Push<R, E, ReadonlyArray<A>>;
67
65
  export function combineLatest<R, E, A>(streams: Iterable<Push<R, E, A>>): Push<R, E, ReadonlyArray<A>> {
68
66
  return Push((emitter) =>
69
67
  Do((Δ) => {
70
68
  const size = streams.size;
71
69
  const ref: Array<A> = Δ(IO(Array(size)));
72
- const latch = Δ(CountdownLatch(size));
73
70
  const emitIfReady = IO(ref.filter((a) => a != null)).flatMap((as) =>
74
- as.length === size ? emitter.emit(as) : IO.unit,
71
+ as.length === size ? emitter.event(as) : IO.unit,
75
72
  );
76
73
  Δ(
77
- IO.foreachWithIndex(
78
- streams,
79
- (i, stream) =>
80
- stream.run(
81
- Emitter(
82
- (value) => IO((ref[i] = value)) > emitIfReady,
83
- (cause) => emitter.failCause(cause),
84
- latch.countDown,
85
- ),
86
- ).forkScoped,
74
+ IO.foreachConcurrent(streams.zipWithIndex, ([i, stream]) =>
75
+ stream.run(
76
+ Sink(
77
+ (value) => IO((ref[i] = value)) > emitIfReady,
78
+ (cause) => emitter.error(cause),
79
+ ),
80
+ ),
87
81
  ),
88
82
  );
89
- Δ(latch.await > emitter.end);
90
83
  }),
91
84
  );
92
85
  }
@@ -105,28 +98,7 @@ export function combineLatestWith<A, R1, E1, B, C>(that: Push<R1, E1, B>, f: (a:
105
98
  */
106
99
  export function debounce(duration: Lazy<Duration>) {
107
100
  return <R, E, A>(self: Push<R, E, A>): Push<R, E, A> => {
108
- return Push((emitter) =>
109
- Do((Δ) => {
110
- const ref = Δ(Ref.Synchronized.make<Fiber<never, unknown> | null>(null));
111
- const latch = Δ(CountdownLatch(1));
112
- Δ(
113
- self.run(
114
- Emitter(
115
- (value) =>
116
- ref.updateIO((previous) =>
117
- Do((Δ) => {
118
- Δ(IO.defer(previous ? previous.interrupt : latch.increment));
119
- return Δ(IO.acquireRelease(emitter.emit(value).delay(duration), () => latch.countDown).forkScoped);
120
- }),
121
- ),
122
- (cause) => emitter.failCause(cause),
123
- latch.countDown,
124
- ),
125
- ).forkScoped,
126
- );
127
- Δ(latch.await > emitter.end);
128
- }),
129
- );
101
+ return self.switchMapIO((a) => IO.succeedNow(a).delay(duration));
130
102
  };
131
103
  }
132
104
 
@@ -137,12 +109,88 @@ export function defer<R, E, A>(self: Lazy<Push<R, E, A>>): Push<R, E, A> {
137
109
  return Push((emitter) => IO(self).flatMap((push) => push.run(emitter)));
138
110
  }
139
111
 
112
+ /**
113
+ * @tsplus pipeable fncts.io.Push exhaustMap
114
+ */
115
+ export function exhaustMap<A, R1, E1, B>(f: (a: A) => Push<R1, E1, B>) {
116
+ return <R, E>(self: Push<R, E, A>): Push<R | R1, E | E1, B> => {
117
+ return Push((sink) => withExhaust((fork) => self.run(Sink((a) => fork(f(a).run(sink)), sink.error))));
118
+ };
119
+ }
120
+
121
+ /**
122
+ * @tsplus pipeable fncts.io.Push exhaustMapIO
123
+ */
124
+ export function exhaustMapIO<A, R1, E1, B>(f: (a: A) => IO<R1, E1, B>) {
125
+ return <R, E>(self: Push<R, E, A>): Push<R | R1, E | E1, B> => {
126
+ return self.exhaustMap((a) => Push.fromIO(f(a)));
127
+ };
128
+ }
129
+
130
+ /**
131
+ * @tsplus pipeable fncts.io.Push filterIO
132
+ */
133
+ export function filterIO<A, R1, E1>(predicate: (a: A) => IO<R1, E1, boolean>) {
134
+ return <R, E>(self: Push<R, E, A>): Push<R | R1, E | E1, A> => {
135
+ return Push((sink) =>
136
+ self.run(
137
+ Sink(
138
+ (a) =>
139
+ predicate(a)
140
+ .flatMap((b) => (b ? sink.event(a) : IO.unit))
141
+ .catchAllCause(sink.error),
142
+ sink.error,
143
+ ),
144
+ ),
145
+ );
146
+ };
147
+ }
148
+
149
+ /**
150
+ * @tsplus pipeable fncts.io.Push filterMapIO
151
+ */
152
+ export function filterMapIO<A, R1, E1, B>(f: (a: A) => IO<R1, E1, Maybe<B>>) {
153
+ return <R, E>(self: Push<R, E, A>): Push<R | R1, E | E1, B> => {
154
+ return Push((sink) =>
155
+ self.run(
156
+ Sink(
157
+ (a) =>
158
+ f(a)
159
+ .flatMap((mb) => mb.match(() => IO.unit, sink.event))
160
+ .catchAllCause(sink.error),
161
+ sink.error,
162
+ ),
163
+ ),
164
+ );
165
+ };
166
+ }
167
+
168
+ /**
169
+ * @tsplus pipeable fncts.io.Push filter
170
+ */
171
+ export function filter<A, B extends A>(refinement: Refinement<A, B>): <R, E>(self: Push<R, E, A>) => Push<R, E, B>;
172
+ export function filter<A>(predicate: Predicate<A>): <R, E>(self: Push<R, E, A>) => Push<R, E, A>;
173
+ export function filter<A>(predicate: Predicate<A>) {
174
+ return <R, E>(self: Push<R, E, A>): Push<R, E, A> => {
175
+ return Push((sink) => self.run(Sink((a) => (predicate(a) ? sink.event(a) : IO.unit), sink.error)));
176
+ };
177
+ }
178
+
179
+ /**
180
+ * @tsplus pipeable fncts.io.Push filterMap
181
+ */
182
+ export function filterMap<A, B>(f: (a: A) => Maybe<B>) {
183
+ return <R, E>(self: Push<R, E, A>): Push<R, E, B> => {
184
+ return Push((sink) => self.run(Sink((a) => f(a).match(() => IO.unit, sink.event), sink.error)));
185
+ };
186
+ }
187
+
140
188
  /**
141
189
  * @tsplus pipeable fncts.io.Push flatMapConcurrentBounded
142
190
  */
143
191
  export function flatMapConcurrentBounded<A, R1, E1, B>(f: (a: A) => Push<R1, E1, B>, concurrency: number) {
144
192
  return <R, E>(self: Push<R, E, A>): Push<R | R1, E | E1, B> => {
145
- return Push(<R2>(emitter: Emitter<R | R1 | R2, E | E1, B>) =>
193
+ return Push(<R2>(emitter: Sink<R | R1 | R2, E | E1, B>) =>
146
194
  Do((Δ) => {
147
195
  const semaphore = Δ(Semaphore(concurrency));
148
196
  Δ(self.flatMapConcurrentUnbounded((a) => f(a).transform((io) => semaphore.withPermit(io))).run(emitter));
@@ -156,22 +204,7 @@ export function flatMapConcurrentBounded<A, R1, E1, B>(f: (a: A) => Push<R1, E1,
156
204
  */
157
205
  export function flatMapConcurrentUnbounded<A, R1, E1, B>(f: (a: A) => Push<R1, E1, B>) {
158
206
  return <R, E>(self: Push<R, E, A>): Push<R | R1, E | E1, B> => {
159
- return Push((emitter) =>
160
- Do((Δ) => {
161
- const latch = Δ(CountdownLatch(1));
162
- Δ(
163
- self.run(
164
- Emitter(
165
- (value) =>
166
- latch.increment > f(value).run(Emitter(emitter.emit, emitter.failCause, latch.countDown)).forkScoped,
167
- emitter.failCause,
168
- latch.countDown,
169
- ),
170
- ).forkScoped,
171
- );
172
- Δ(latch.await > emitter.end);
173
- }),
174
- );
207
+ return Push((sink) => withUnboundedConcurrency((fork) => self.run(Sink((a) => fork(f(a).run(sink)), sink.error))));
175
208
  };
176
209
  }
177
210
 
@@ -211,12 +244,11 @@ export function flatten<R, E, R1, E1, A>(self: Push<R, E, Push<R1, E1, A>>): Pus
211
244
  * @tsplus static fncts.io.PushOps fromIO
212
245
  */
213
246
  export function fromIO<R, E, A>(io: Lazy<IO<R, E, A>>): Push<R, E, A> {
214
- return Push(
215
- (emitter) =>
216
- IO.defer(io).matchCauseIO(
217
- (cause) => emitter.failCause(cause),
218
- (value) => emitter.emit(value),
219
- ) > emitter.end,
247
+ return Push((emitter) =>
248
+ IO.defer(io).matchCauseIO(
249
+ (cause) => emitter.error(cause),
250
+ (value) => emitter.event(value),
251
+ ),
220
252
  );
221
253
  }
222
254
 
@@ -224,17 +256,20 @@ export function fromIO<R, E, A>(io: Lazy<IO<R, E, A>>): Push<R, E, A> {
224
256
  * @tsplus static fncts.io.PushOps fromAsyncIterable
225
257
  */
226
258
  export function fromAsyncIterable<A>(iterable: AsyncIterable<A>): Push<never, never, A> {
227
- return Push((emitter) => IO.defer(fromAsyncIterableLoop(iterable[Symbol.asyncIterator](), emitter)));
259
+ return Push(<R>(sink: Sink<R, never, A>) =>
260
+ IO.asyncIO<R, never, void>((cb) => IO.defer(fromAsyncIterableLoop(iterable[Symbol.asyncIterator](), sink, cb))),
261
+ );
228
262
  }
229
263
 
230
264
  function fromAsyncIterableLoop<A, R>(
231
265
  iterator: AsyncIterator<A>,
232
- emitter: Emitter<R, never, A>,
266
+ sink: Sink<R, never, A>,
267
+ cb: (io: UIO<void>) => void,
233
268
  __tsplusTrace?: string,
234
269
  ): IO<R, never, void> {
235
270
  return IO.fromPromiseHalt(iterator.next).matchCauseIO(
236
- (cause) => emitter.failCause(cause) > emitter.end,
237
- (result) => (result.done ? emitter.end : emitter.emit(result.value) > fromAsyncIterableLoop(iterator, emitter)),
271
+ (cause) => sink.error(cause),
272
+ (result) => (result.done ? IO(cb(IO.unit)) : sink.event(result.value) > fromAsyncIterableLoop(iterator, sink, cb)),
238
273
  );
239
274
  }
240
275
 
@@ -242,13 +277,19 @@ function fromAsyncIterableLoop<A, R>(
242
277
  * @tsplus static fncts.io.PushOps fromIterable
243
278
  */
244
279
  export function fromIterable<A>(iterable: Iterable<A>): Push<never, never, A> {
245
- return Push((emitter) => IO.defer(fromIterableLoop(iterable[Symbol.iterator](), emitter)));
280
+ return Push(<R>(sink: Sink<R, never, A>) =>
281
+ IO.asyncIO<R, never, void>((cb) => IO.defer(fromIterableLoop(iterable[Symbol.iterator](), sink, cb))),
282
+ );
246
283
  }
247
284
 
248
- function fromIterableLoop<A, R>(iterator: Iterator<A>, emitter: Emitter<R, never, A>): IO<R, never, void> {
285
+ function fromIterableLoop<A, R>(
286
+ iterator: Iterator<A>,
287
+ sink: Sink<R, never, A>,
288
+ cb: (io: UIO<void>) => void,
289
+ ): IO<R, never, void> {
249
290
  return IO.defer(() => {
250
291
  const value = iterator.next();
251
- return value.done ? emitter.end : emitter.emit(value.value) > fromIterableLoop(iterator, emitter);
292
+ return value.done ? IO(cb(IO.unit)) : sink.event(value.value) > fromIterableLoop(iterator, sink, cb);
252
293
  });
253
294
  }
254
295
 
@@ -260,12 +301,11 @@ export function multicast<R, E, A>(self: Push<R, E, A>): Push<R, E, A> {
260
301
  }
261
302
 
262
303
  interface MulticastObserver<E, A> {
263
- readonly emitter: Emitter<any, E, A>;
304
+ readonly sink: Sink<any, E, A>;
264
305
  readonly environment: Environment<any>;
265
- readonly future: Future<never, void>;
266
306
  }
267
307
 
268
- export class Multicast<R, E, A> implements Push<R, E, A>, Emitter<never, E, A> {
308
+ export class Multicast<R, E, A> implements Push<R, E, A>, Sink<never, E, A> {
269
309
  readonly [PushTypeId]: PushTypeId = PushTypeId;
270
310
  declare [PushVariance]: {
271
311
  readonly _R: (_: never) => R;
@@ -276,67 +316,59 @@ export class Multicast<R, E, A> implements Push<R, E, A>, Emitter<never, E, A> {
276
316
  protected fiber: Fiber<never, unknown> | undefined;
277
317
  constructor(readonly push: Push<R, E, A>) {}
278
318
 
279
- run<R1>(emitter: Emitter<R1, E, A>): IO<R | R1 | Scope, never, unknown> {
319
+ run<R1>(sink: Sink<R1, E, A>): IO<R | R1, never, void> {
280
320
  return Do((Δ) => {
281
321
  const environment = Δ(IO.environment<R1>());
282
- const future = Δ(Future.make<never, void>());
283
322
  Δ(
284
323
  IO.defer(() => {
285
- this.observers.push({ emitter, environment, future });
286
- if (this.fiber) {
287
- return IO.unit;
288
- } else {
289
- return this.push
290
- .run(this)
291
- .schedule(Schedule.asap)
292
- .forkScoped.tap((fiber) => IO((this.fiber = fiber)));
324
+ let io: URIO<R, void> = IO.unit;
325
+ if (this.observers.push({ sink: sink, environment }) === 1) {
326
+ io = this.push.run(this).forkDaemon.flatMap((fiber) => IO((this.fiber = fiber)));
293
327
  }
328
+ return io > this.fiber!.await.ensuring(this.removeSink(sink));
294
329
  }),
295
330
  );
296
- return Δ(future.await);
297
331
  });
298
332
  }
299
333
 
300
- emit(value: A) {
301
- return IO.defer(IO.foreachDiscard(this.observers.slice(), (observer) => this.runEvent(value, observer)));
302
- }
303
-
304
- failCause(cause: Cause<E>) {
305
- return (
306
- IO.defer(IO.foreachDiscard(this.observers.slice(), (observer) => this.runFailCause(cause, observer))) >
307
- IO.defer(this.cleanup())
308
- );
334
+ event(value: A) {
335
+ return IO.defer(IO.foreachDiscard(this.observers.slice(), (observer) => this.runValue(value, observer)));
309
336
  }
310
337
 
311
- get end() {
312
- return (
313
- IO.defer(IO.foreachDiscard(this.observers.slice(), (observer) => this.runEnd(observer))) >
314
- IO.defer(this.cleanup())
315
- );
338
+ error(cause: Cause<E>) {
339
+ return IO.defer(IO.foreachDiscard(this.observers.slice(), (observer) => this.runError(cause, observer)));
316
340
  }
317
341
 
318
- protected runEvent(value: A, observer: MulticastObserver<E, A>) {
319
- return observer.emitter
320
- .emit(value)
321
- .tapErrorCause((cause) => this.runFailCause(cause, observer))
322
- .provideEnvironment(observer.environment);
342
+ protected runValue(value: A, observer: MulticastObserver<E, A>) {
343
+ return observer.sink
344
+ .event(value)
345
+ .provideEnvironment(observer.environment)
346
+ .catchAllCause(() => this.removeSink(observer.sink));
323
347
  }
324
348
 
325
- protected runFailCause(cause: Cause<E>, observer: MulticastObserver<E, A>) {
326
- this.observers.splice(this.observers.indexOf(observer), 1);
327
- return observer.emitter.failCause(cause).fulfill(observer.future).provideEnvironment(observer.environment);
349
+ protected runError(cause: Cause<E>, observer: MulticastObserver<E, A>) {
350
+ return observer.sink
351
+ .error(cause)
352
+ .provideEnvironment(observer.environment)
353
+ .catchAllCause(() => this.removeSink(observer.sink));
328
354
  }
329
355
 
330
- protected runEnd(observer: MulticastObserver<E, A>) {
331
- this.observers.splice(this.observers.indexOf(observer), 1);
332
- return observer.emitter.end.fulfill(observer.future).provideEnvironment(observer.environment);
333
- }
334
-
335
- protected cleanup() {
336
- if (this.fiber) {
337
- return this.fiber.interrupt > IO((this.fiber = undefined));
338
- }
339
- return IO.unit;
356
+ protected removeSink(sink: Sink<any, E, A>) {
357
+ return IO.defer(() => {
358
+ if (this.observers.length === 0) {
359
+ return IO.unit;
360
+ }
361
+ const index = this.observers.findIndex((observer) => observer.sink === sink);
362
+ if (index > -1) {
363
+ this.observers.splice(index, 1);
364
+ if (this.observers.length === 0) {
365
+ const interrupt = this.fiber!.interrupt;
366
+ this.fiber = undefined;
367
+ return interrupt;
368
+ }
369
+ }
370
+ return IO.unit;
371
+ });
340
372
  }
341
373
  }
342
374
 
@@ -348,87 +380,26 @@ export function hold<R, E, A>(self: Push<R, E, A>): Push<R, E, A> {
348
380
  }
349
381
 
350
382
  export class Hold<R, E, A> extends Multicast<R, E, A> {
351
- readonly value = new AtomicReference(Nothing<A>());
352
- protected pendingEmitters: Array<readonly [Emitter<unknown, E, A>, Array<A>]> = [];
353
- protected scheduledFiber: Fiber<any, any> | null = null;
383
+ readonly current = new AtomicReference(Nothing<A>());
354
384
 
355
- constructor(readonly push: Push<R, E, A>) {
385
+ constructor(public push: Push<R, E, A>) {
356
386
  super(push);
357
387
  }
358
388
 
359
- run<R1>(emitter: Emitter<R1, E, A>): IO<R | R1 | Scope, never, void> {
360
- if (this.shouldScheduleFlush()) {
361
- return this.scheduleFlush(emitter).flatMap(() => super.run(emitter));
362
- }
389
+ run<R1>(sink: Sink<R1, E, A>): IO<R | R1, never, void> {
390
+ const current = this.current.get;
363
391
 
364
- const value = this.value.get;
365
- if (value.isJust() && this.observers.length === 0) {
366
- return emitter.emit(value.value).flatMap(() => super.run(emitter));
392
+ if (current.isJust()) {
393
+ return sink.event(current.value) > super.run(sink);
367
394
  }
368
395
 
369
- return super.run(emitter);
396
+ return super.run(sink);
370
397
  }
371
398
 
372
- emit(value: A) {
399
+ event(value: A): IO<never, never, void> {
373
400
  return IO.defer(() => {
374
- this.addValue(value);
375
- return this.flushPending().flatMap(() => super.emit(value));
376
- });
377
- }
378
-
379
- failCause(cause: Cause<E>) {
380
- return IO.defer(this.flushPending().flatMap(() => super.failCause(cause)));
381
- }
382
-
383
- get end() {
384
- return IO.defer(this.flushPending().flatMap(() => super.end));
385
- }
386
-
387
- protected shouldScheduleFlush() {
388
- return this.value.get.isJust() && this.observers.length > 0;
389
- }
390
-
391
- protected scheduleFlush<R>(observer: Emitter<R, E, A>) {
392
- this.pendingEmitters.push([
393
- observer,
394
- this.value.get.match(
395
- () => [],
396
- (a) => [a],
397
- ),
398
- ]);
399
-
400
- const interrupt = this.scheduledFiber ? this.scheduledFiber.interruptAsFork(FiberId.none) : IO.unit;
401
- this.scheduledFiber = null;
402
-
403
- return (IO.yieldNow > interrupt.flatMap(() => this.flushPending())).forkScoped.tap((fiber) =>
404
- IO((this.scheduledFiber = fiber)),
405
- );
406
- }
407
-
408
- protected flushPending() {
409
- if (this.pendingEmitters.length === 0) {
410
- return IO.unit;
411
- }
412
-
413
- const emitters = this.pendingEmitters;
414
- this.pendingEmitters = [];
415
-
416
- return IO.foreachDiscard(emitters, (pendingEmitter) => {
417
- return IO.defer(() => {
418
- const [emitter, values] = pendingEmitter;
419
- const observer = this.observers.find((observer) => observer.emitter === emitter);
420
- if (!observer) {
421
- return IO.unit;
422
- }
423
- return IO.foreachDiscard(values, (value) => this.runEvent(value, observer));
424
- });
425
- });
426
- }
427
-
428
- protected addValue(value: A) {
429
- this.value.set(Just(value));
430
- this.pendingEmitters.forEach(([, values]) => {
431
- values.push(value);
401
+ this.current.set(Just(value));
402
+ return super.event(value);
432
403
  });
433
404
  }
434
405
  }
@@ -449,10 +420,9 @@ export function mapError<E, E1>(f: (e: E) => E1) {
449
420
  return <R, A>(self: Push<R, E, A>): Push<R, E1, A> => {
450
421
  return Push((emitter) =>
451
422
  self.run(
452
- Emitter(
453
- (value) => emitter.emit(value),
454
- (cause) => emitter.failCause(cause.map(f)),
455
- emitter.end,
423
+ Sink(
424
+ (value) => emitter.event(value),
425
+ (cause) => emitter.error(cause.map(f)),
456
426
  ),
457
427
  ),
458
428
  );
@@ -466,10 +436,9 @@ export function mapErrorCause<E, E1>(f: (cause: Cause<E>) => Cause<E1>) {
466
436
  return <R, A>(self: Push<R, E, A>): Push<R, E1, A> => {
467
437
  return Push((emitter) =>
468
438
  self.run(
469
- Emitter(
470
- (value) => emitter.emit(value),
471
- (cause) => emitter.failCause(f(cause)),
472
- emitter.end,
439
+ Sink(
440
+ (value) => emitter.event(value),
441
+ (cause) => emitter.error(f(cause)),
473
442
  ),
474
443
  ),
475
444
  );
@@ -483,14 +452,13 @@ export function mapIO<A, R1, E1, B>(f: (a: A) => IO<R1, E1, B>) {
483
452
  return <R, E>(self: Push<R, E, A>): Push<R | R1, E | E1, B> =>
484
453
  Push((emitter) =>
485
454
  self.run(
486
- Emitter(
455
+ Sink(
487
456
  (value) =>
488
457
  f(value).matchCauseIO(
489
- (cause) => emitter.failCause(cause),
490
- (b) => emitter.emit(b),
458
+ (cause) => emitter.error(cause),
459
+ (b) => emitter.event(b),
491
460
  ),
492
- (cause) => emitter.failCause(cause),
493
- emitter.end,
461
+ (cause) => emitter.error(cause),
494
462
  ),
495
463
  ),
496
464
  );
@@ -513,23 +481,10 @@ export function mergeAll<A extends ReadonlyArray<Push<any, any, any>>>(
513
481
  ): Push<Push.EnvironmentOf<A[number]>, Push.ErrorOf<A[number]>, Push.ValueOf<A[number]>>;
514
482
  export function mergeAll<R, E, A>(streams: Iterable<Push<R, E, A>>): Push<R, E, A>;
515
483
  export function mergeAll<R, E, A>(streams: Iterable<Push<R, E, A>>): Push<R, E, A> {
516
- return Push((emitter) =>
517
- Do((Δ) => {
518
- const latch = Δ(CountdownLatch(streams.size));
519
- Δ(
520
- streams.traverseIOConcurrent(
521
- (stream) =>
522
- stream.run(
523
- Emitter(
524
- (value) => emitter.emit(value),
525
- (cause) => emitter.failCause(cause),
526
- latch.countDown,
527
- ),
528
- ).forkScoped,
529
- ),
530
- );
531
- Δ(latch.await > emitter.end);
532
- }),
484
+ return Push((sink) =>
485
+ IO.foreachConcurrentDiscard(streams, (stream) =>
486
+ stream.run(Sink(sink.event, (cause) => (cause.isInterruptedOnly ? IO.unit : sink.error(cause)))),
487
+ ),
533
488
  );
534
489
  }
535
490
 
@@ -541,13 +496,14 @@ export function observe<A, R1, E1>(f: (a: A) => IO<R1, E1, void>, __tsplusTrace?
541
496
  return Do((Δ) => {
542
497
  const future = Δ(Future.make<E | E1, void>());
543
498
  const fiber = Δ(
544
- self.run(
545
- Emitter(
546
- (a) => f(a).catchAllCause((cause) => future.failCause(cause)),
547
- (cause) => future.failCause(cause),
548
- future.succeed(undefined),
549
- ),
550
- ).forkScoped,
499
+ self
500
+ .run(
501
+ Sink(
502
+ (a) => f(a).catchAllCause((cause) => future.failCause(cause)),
503
+ (cause) => future.failCause(cause),
504
+ ),
505
+ )
506
+ .flatMap(() => future.succeed(undefined)).forkScoped,
551
507
  );
552
508
 
553
509
  Δ(future.await);
@@ -573,18 +529,7 @@ export function repeatIOMaybe<R, E, A>(io: IO<R, Maybe<E>, A>, __tsplusTrace?: s
573
529
  export function runCollect<R, E, A>(self: Push<R, E, A>): IO<R | Scope, E, Conc<A>> {
574
530
  return IO.defer(() => {
575
531
  const out: Array<A> = [];
576
- return Future.make<E, void>().flatMap(
577
- (future) =>
578
- self.run(
579
- Emitter(
580
- (value) => IO(out.push(value)),
581
- (cause) => future.failCause(cause),
582
- future.succeed(undefined),
583
- ),
584
- ) >
585
- future.await >
586
- IO(Conc.fromArray(out)),
587
- );
532
+ return self.observe((a) => IO(out.push(a))).as(Conc.fromArray(out));
588
533
  });
589
534
  }
590
535
 
@@ -592,28 +537,18 @@ export function runCollect<R, E, A>(self: Push<R, E, A>): IO<R | Scope, E, Conc<
592
537
  * @tsplus getter fncts.io.Push runDrain
593
538
  */
594
539
  export function runDrain<R, E, A>(self: Push<R, E, A>): IO<R | Scope, E, void> {
595
- return Future.make<E, void>().flatMap(
596
- (future) =>
597
- self.run(
598
- Emitter(
599
- () => IO.unit,
600
- (cause) => future.failCause(cause),
601
- future.succeed(undefined),
602
- ),
603
- ) > future.await,
604
- );
540
+ return self.observe(() => IO.unit);
605
541
  }
606
542
 
607
543
  /**
608
544
  * @tsplus static fncts.io.PushOps scoped
609
545
  */
610
546
  export function scoped<R, E, A>(io: Lazy<IO<R, E, A>>, __tsplusTrace?: string): Push<Exclude<R, Scope>, E, A> {
611
- return Push(
612
- (emitter) =>
613
- IO.defer(io).scoped.matchCauseIO(
614
- (cause) => emitter.failCause(cause),
615
- (value) => emitter.emit(value),
616
- ) > emitter.end,
547
+ return Push((emitter) =>
548
+ IO.defer(io).scoped.matchCauseIO(
549
+ (cause) => emitter.error(cause),
550
+ (value) => emitter.event(value),
551
+ ),
617
552
  );
618
553
  }
619
554
 
@@ -625,34 +560,29 @@ export function succeed<A>(value: Lazy<A>): Push<never, never, A> {
625
560
  }
626
561
 
627
562
  /**
628
- * @tsplus pipeable fncts.io.PushOps switchMap
563
+ * @tsplus pipeable fncts.io.Push switchMap
629
564
  */
630
565
  export function switchMap<A, R1, E1, B>(f: (a: A) => Push<R1, E1, B>) {
631
566
  return <R, E>(self: Push<R, E, A>): Push<R | R1, E | E1, B> => {
632
- return Push(<R2>(emitter: Emitter<R2, E | E1, B>) =>
633
- Do((Δ) => {
634
- const current = Δ(Ref.Synchronized.make<Fiber<never, void> | null>(null));
635
- const latch = Δ(CountdownLatch(1));
636
- const innerEmitter = Emitter<R2, E | E1, B>(
637
- (value) => emitter.emit(value),
638
- (cause) => current.set(null) > emitter.failCause(cause),
639
- latch.countDown,
640
- );
641
- Δ(
642
- self.run(
643
- Emitter(
644
- (value) =>
645
- current.updateIO((fiber) =>
646
- (fiber ? fiber.interrupt : latch.increment).zipRight(f(value).run(innerEmitter).forkScoped),
647
- ),
648
- (cause) => emitter.failCause(cause),
649
- latch.countDown,
650
- ),
651
- ),
652
- );
653
- Δ(latch.await > emitter.end);
654
- }),
655
- );
567
+ return Push((sink) => withSwitch((fork) => self.run(Sink((a) => fork(f(a).run(sink)), sink.error))));
568
+ };
569
+ }
570
+
571
+ /**
572
+ * @tsplus pipeable fncts.io.Push switchMapIO
573
+ */
574
+ export function switchMapIO<A, R1, E1, B>(f: (a: A) => IO<R1, E1, B>) {
575
+ return <R, E>(self: Push<R, E, A>): Push<R | R1, E | E1, B> => {
576
+ return self.switchMap((a) => Push.fromIO(f(a)));
577
+ };
578
+ }
579
+
580
+ /**
581
+ * @tsplus pipeable fncts.io.Push tap
582
+ */
583
+ export function tap<A, R1, E1, B>(f: (a: A) => IO<R1, E1, B>) {
584
+ return <R, E>(self: Push<R, E, A>): Push<R | R1, E | E1, A> => {
585
+ return Push((sink) => self.run(Sink((a) => f(a).matchCauseIO(sink.error, () => sink.event(a)), sink.error)));
656
586
  };
657
587
  }
658
588
 
@@ -666,11 +596,11 @@ export function transform<R1 = never>(f: <R, E, A>(io: IO<R, E, A>) => IO<R | R1
666
596
  function unfoldLoop<S, A, R1>(
667
597
  s: S,
668
598
  f: (s: S) => Maybe<readonly [A, S]>,
669
- emitter: Emitter<R1, never, A>,
599
+ emitter: Sink<R1, never, A>,
670
600
  ): IO<R1, never, void> {
671
601
  return f(s).match(
672
- () => emitter.end,
673
- ([a, s]) => emitter.emit(a) > unfoldLoop(s, f, emitter),
602
+ () => IO.unit,
603
+ ([a, s]) => emitter.event(a) > unfoldLoop(s, f, emitter),
674
604
  );
675
605
  }
676
606
 
@@ -684,16 +614,16 @@ export function unfold<S, A>(s: S, f: (s: S) => Maybe<readonly [A, S]>): Push<ne
684
614
  function unfoldIOLoop<S, R, E, A, R1>(
685
615
  s: S,
686
616
  f: (s: S) => IO<R, E, Maybe<readonly [A, S]>>,
687
- emitter: Emitter<R1, E, A>,
617
+ emitter: Sink<R1, E, A>,
688
618
  ): IO<R | R1, never, void> {
689
619
  return f(s)
690
620
  .flatMap((result) =>
691
621
  result.match(
692
- () => emitter.end,
693
- ([a, s]) => emitter.emit(a) > unfoldIOLoop(s, f, emitter),
622
+ () => IO.unit,
623
+ ([a, s]) => emitter.event(a) > unfoldIOLoop(s, f, emitter),
694
624
  ),
695
625
  )
696
- .catchAllCause((cause) => emitter.failCause(cause));
626
+ .catchAllCause((cause) => emitter.error(cause));
697
627
  }
698
628
 
699
629
  /**
@@ -708,18 +638,21 @@ export function unfoldIO<S, R, E, A>(s: S, f: (s: S) => IO<R, E, Maybe<readonly
708
638
  */
709
639
  export function untilFuture<E1, B>(future: Future<E1, B>) {
710
640
  return <R, E, A>(self: Push<R, E, A>): Push<R, E | E1, A> => {
711
- return Push((emitter) =>
712
- Do((Δ) => {
713
- const futureFiber = Δ(
714
- future.await
715
- .matchCauseIO(
716
- (cause) => emitter.failCause(cause),
717
- () => IO.unit,
718
- )
719
- .zipRight(earlyExit).forkScoped,
720
- );
721
- const streamFiber = Δ(self.run(emitter).forkScoped);
722
- Δ(Fiber.joinAll([futureFiber, streamFiber])(onEarlyExit(emitter.end)));
641
+ return Push(<R1>(sink: Sink<R1, E | E1, A>) =>
642
+ IO.asyncIO<R | R1, never, void>((cb) => {
643
+ const exit = IO(cb(IO.unit));
644
+ return Do((Δ) => {
645
+ const streamFiber = Δ(self.run(sink).fork);
646
+ const futureFiber = Δ(
647
+ future.await
648
+ .matchCauseIO(
649
+ (cause) => sink.error(cause),
650
+ () => IO.unit,
651
+ )
652
+ .zipRight(exit).fork,
653
+ );
654
+ Δ(Fiber.joinAll([streamFiber, futureFiber]));
655
+ });
723
656
  }),
724
657
  );
725
658
  };
@@ -730,19 +663,21 @@ export function untilFuture<E1, B>(future: Future<E1, B>) {
730
663
  */
731
664
  export function untilPush<R1, E1, B>(signal: Push<R1, E1, B>) {
732
665
  return <R, E, A>(self: Push<R, E, A>): Push<R | R1, E | E1, A> => {
733
- return Push((emitter) =>
734
- Do((Δ) => {
735
- const signalFiber = Δ(
736
- signal.run(
737
- Emitter(
738
- () => earlyExit,
739
- (cause) => emitter.failCause(cause),
740
- earlyExit,
741
- ),
742
- ).forkScoped,
743
- );
744
- const streamFiber = Δ(self.run(emitter).forkScoped);
745
- Δ(Fiber.joinAll([signalFiber, streamFiber])(onEarlyExit(emitter.end)));
666
+ return Push(<R2>(sink: Sink<R2, E | E1, A>) =>
667
+ IO.asyncIO<R | R1 | R2, never, void>((cb) => {
668
+ const exit = IO(cb(IO.unit));
669
+ return Do((Δ) => {
670
+ const signalFiber = Δ(
671
+ signal.run(
672
+ Sink(
673
+ () => exit,
674
+ (cause) => sink.error(cause),
675
+ ),
676
+ ).fork,
677
+ );
678
+ const streamFiber = Δ(self.run(sink).fork);
679
+ Δ(Fiber.joinAll([signalFiber, streamFiber]));
680
+ });
746
681
  }),
747
682
  );
748
683
  };