@fncts/io 0.0.8 → 0.0.11

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