@typed/fx 1.12.2 → 1.12.3

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.
package/src/keyed.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as MutableHashMap from '@effect/data/MutableHashMap'
2
2
  import * as Option from '@effect/data/Option'
3
3
  import * as ReadonlyArray from '@effect/data/ReadonlyArray'
4
- import { Equivalence } from '@effect/data/typeclass/Equivalence'
4
+ import * as Equivalence from '@effect/data/typeclass/Equivalence'
5
5
  import * as Effect from '@effect/io/Effect'
6
6
  import * as Fiber from '@effect/io/Fiber'
7
7
  import fastDeepEqual from 'fast-deep-equal'
@@ -12,18 +12,19 @@ import { Subject, makeHoldSubject } from './Subject.js'
12
12
  import { Cause } from './externals.js'
13
13
  import { ScopedFork, withScopedFork } from './helpers.js'
14
14
 
15
- export function keyed<R, E, A, R2, E2, B>(
15
+ export function keyed<R, E, A, R2, E2, B, C>(
16
16
  fx: Fx<R, E, readonly A[]>,
17
- f: (a: Fx<never, never, A>, initial: A) => Fx<R2, E2, B>,
18
- eq: Equivalence<A> = fastDeepEqual,
17
+ f: (fx: RefSubject<never, A>) => Fx<R2, E2, B>,
18
+ getKey: (a: A) => C,
19
19
  ): Fx<R | R2, E | E2, readonly B[]> {
20
20
  return Fx(<R3>(sink: Sink<R3, E | E2, readonly B[]>) =>
21
21
  withScopedFork((fork) =>
22
22
  Effect.gen(function* ($) {
23
- const state = createKeyedState<A, B>()
23
+ const state = createKeyedState<A, B, C>()
24
+ const eq = Equivalence.make((x: A, y: A) => fastDeepEqual(getKey(x), getKey(y)))
24
25
  const difference = ReadonlyArray.difference(eq)
25
26
  const intersection = ReadonlyArray.intersection(eq)
26
- const emit = emitWhenReady(state)
27
+ const emit = emitWhenReady(state, getKey)
27
28
 
28
29
  // Let output emit to the sink
29
30
  const fiber = yield* $(fork(state.output.run(sink)))
@@ -36,6 +37,7 @@ export function keyed<R, E, A, R2, E2, B>(
36
37
  updateState({
37
38
  state,
38
39
  updated: as,
40
+ getKey,
39
41
  f,
40
42
  fork,
41
43
  difference,
@@ -60,17 +62,17 @@ export function keyed<R, E, A, R2, E2, B>(
60
62
  )
61
63
  }
62
64
 
63
- type KeyedState<A, B> = {
65
+ type KeyedState<A, B, C> = {
64
66
  previous: readonly A[]
65
67
  ended: boolean
66
68
 
67
- readonly subjects: MutableHashMap.MutableHashMap<A, Subject<never, A>>
68
- readonly fibers: MutableHashMap.MutableHashMap<A, Fiber.RuntimeFiber<never, void>>
69
- readonly values: MutableHashMap.MutableHashMap<A, B>
69
+ readonly subjects: MutableHashMap.MutableHashMap<C, Subject<never, A>>
70
+ readonly fibers: MutableHashMap.MutableHashMap<C, Fiber.RuntimeFiber<never, void>>
71
+ readonly values: MutableHashMap.MutableHashMap<C, B>
70
72
  readonly output: Subject<never, readonly B[]>
71
73
  }
72
74
 
73
- function createKeyedState<A, B>(): KeyedState<A, B> {
75
+ function createKeyedState<A, B, C>(): KeyedState<A, B, C> {
74
76
  return {
75
77
  previous: [],
76
78
  ended: false,
@@ -81,7 +83,7 @@ function createKeyedState<A, B>(): KeyedState<A, B> {
81
83
  }
82
84
  }
83
85
 
84
- function updateState<A, B, R2, E2, R3>({
86
+ function updateState<A, B, C, R2, E2, R3>({
85
87
  state,
86
88
  updated,
87
89
  f,
@@ -90,15 +92,17 @@ function updateState<A, B, R2, E2, R3>({
90
92
  intersection,
91
93
  emit,
92
94
  error,
95
+ getKey,
93
96
  }: {
94
- state: KeyedState<A, B>
97
+ state: KeyedState<A, B, C>
95
98
  updated: readonly A[]
96
- f: (a: Fx<never, never, A>, initial: A) => Fx<R2, E2, B>
99
+ f: (fx: RefSubject<never, A>) => Fx<R2, E2, B>
97
100
  fork: ScopedFork
98
101
  difference: (self: Iterable<A>, that: Iterable<A>) => A[]
99
102
  intersection: (self: Iterable<A>, that: Iterable<A>) => A[]
100
103
  emit: Effect.Effect<never, never, void>
101
104
  error: (e: Cause.Cause<E2>) => Effect.Effect<R3, never, void>
105
+ getKey: (a: A) => C
102
106
  }) {
103
107
  return Effect.gen(function* ($) {
104
108
  const added = difference(updated, state.previous)
@@ -108,15 +112,17 @@ function updateState<A, B, R2, E2, R3>({
108
112
  state.previous = updated
109
113
 
110
114
  // Remove values that are no longer in the stream
111
- yield* $(Effect.forEachParDiscard(removed, (value) => removeValue(state, value)))
115
+ yield* $(Effect.forEachParDiscard(removed, (value) => removeValue(state, value, getKey)))
112
116
 
113
117
  // Add values that are new to the stream
114
118
  yield* $(
115
- Effect.forEachParDiscard(added, (value) => addValue({ state, value, f, fork, emit, error })),
119
+ Effect.forEachParDiscard(added, (value) =>
120
+ addValue({ state, value, f, fork, emit, error, getKey }),
121
+ ),
116
122
  )
117
123
 
118
124
  // Update values that are still in the stream
119
- yield* $(Effect.forEachParDiscard(unchanged, (value) => updateValue(state, value)))
125
+ yield* $(Effect.forEachParDiscard(unchanged, (value) => updateValue(state, value, getKey)))
120
126
 
121
127
  // If nothing was added or removed, emit the current values
122
128
  if (added.length === 0 && removed.length === 0) {
@@ -125,47 +131,51 @@ function updateState<A, B, R2, E2, R3>({
125
131
  })
126
132
  }
127
133
 
128
- function removeValue<A, B>(state: KeyedState<A, B>, value: A) {
134
+ function removeValue<A, B, C>(state: KeyedState<A, B, C>, value: A, getKey: (a: A) => C) {
129
135
  return Effect.gen(function* ($) {
130
- const subject = MutableHashMap.get(state.subjects, value)
136
+ const key = getKey(value)
137
+ const subject = MutableHashMap.get(state.subjects, key)
131
138
 
132
139
  if (Option.isSome(subject)) yield* $(subject.value.end())
133
140
 
134
- const fiber = MutableHashMap.get(state.fibers, value)
141
+ const fiber = MutableHashMap.get(state.fibers, key)
135
142
 
136
143
  if (Option.isSome(fiber)) yield* $(Fiber.interrupt(fiber.value))
137
144
 
138
- MutableHashMap.remove(state.values, value)
139
- MutableHashMap.remove(state.subjects, value)
140
- MutableHashMap.remove(state.fibers, value)
145
+ MutableHashMap.remove(state.values, key)
146
+ MutableHashMap.remove(state.subjects, key)
147
+ MutableHashMap.remove(state.fibers, key)
141
148
  })
142
149
  }
143
150
 
144
- function addValue<A, B, R2, E2, R3>({
151
+ function addValue<A, B, C, R2, E2, R3>({
145
152
  state,
146
153
  value,
147
154
  f,
148
155
  fork,
149
156
  emit,
150
157
  error,
158
+ getKey,
151
159
  }: {
152
- state: KeyedState<A, B>
160
+ state: KeyedState<A, B, C>
153
161
  value: A
154
- f: (a: Fx<never, never, A>, initial: A) => Fx<R2, E2, B>
162
+ f: (fx: RefSubject<never, A>) => Fx<R2, E2, B>
155
163
  fork: ScopedFork
156
164
  emit: Effect.Effect<never, never, void>
157
165
  error: (e: Cause.Cause<E2>) => Effect.Effect<R3, never, void>
166
+ getKey: (a: A) => C
158
167
  }) {
159
168
  return Effect.gen(function* ($) {
169
+ const key = getKey(value)
160
170
  const subject = RefSubject.unsafeMake<never, A>(Effect.succeed(value))
161
- const fx = f(subject, value)
171
+ const fx = f(subject)
162
172
  const fiber = yield* $(
163
173
  fork(
164
174
  fx.run(
165
175
  Sink(
166
176
  (b: B) =>
167
177
  Effect.suspend(() => {
168
- MutableHashMap.set(state.values, value, b)
178
+ MutableHashMap.set(state.values, key, b)
169
179
  return emit
170
180
  }),
171
181
  error,
@@ -174,14 +184,14 @@ function addValue<A, B, R2, E2, R3>({
174
184
  ),
175
185
  )
176
186
 
177
- MutableHashMap.set(state.subjects, value, subject)
178
- MutableHashMap.set(state.fibers, value, fiber)
187
+ MutableHashMap.set(state.subjects, key, subject)
188
+ MutableHashMap.set(state.fibers, key, fiber)
179
189
  })
180
190
  }
181
191
 
182
- function updateValue<A, B>(state: KeyedState<A, B>, value: A) {
192
+ function updateValue<A, B, C>(state: KeyedState<A, B, C>, value: A, getKey: (a: A) => C) {
183
193
  return Effect.gen(function* ($) {
184
- const subject = MutableHashMap.get(state.subjects, value)
194
+ const subject = MutableHashMap.get(state.subjects, getKey(value))
185
195
 
186
196
  // Send the current value
187
197
  if (Option.isSome(subject)) {
@@ -190,10 +200,10 @@ function updateValue<A, B>(state: KeyedState<A, B>, value: A) {
190
200
  })
191
201
  }
192
202
 
193
- function emitWhenReady<A, B>(state: KeyedState<A, B>) {
203
+ function emitWhenReady<A, B, C>(state: KeyedState<A, B, C>, getKey: (a: A) => C) {
194
204
  return Effect.suspend(() => {
195
205
  const values = ReadonlyArray.filterMap(state.previous, (value) =>
196
- MutableHashMap.get(state.values, value),
206
+ MutableHashMap.get(state.values, getKey(value)),
197
207
  )
198
208
 
199
209
  // When all of the values have resolved at least once, emit the output
@@ -205,7 +215,7 @@ function emitWhenReady<A, B>(state: KeyedState<A, B>) {
205
215
  })
206
216
  }
207
217
 
208
- function endAll<A, B>(state: KeyedState<A, B>) {
218
+ function endAll<A, B, C>(state: KeyedState<A, B, C>) {
209
219
  return Effect.gen(function* ($) {
210
220
  yield* $(Effect.forEachParDiscard(state.subjects, ([, subject]) => subject.end()))
211
221
  })