@fncts/io 0.0.35 → 0.0.37

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 (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
  };