1/**
2 * Transactional hash maps for storing and updating key-value pairs inside
3 * Effect transactions.
4 *
5 * A `TxHashMap` stores an immutable `HashMap` in a `TxRef`, so map reads and
6 * writes can commit atomically with other transactional operations. Use it for
7 * shared registries, counters, indexes, and other maps that need safe
8 * read-modify-write sequences alongside related transactional state.
9 *
10 * @since 2.0.0
11 */
12
13import * as Effect from "./Effect.ts"
14import { format } from "./Formatter.ts"
15import { dual } from "./Function.ts"
16import * as HashMap from "./HashMap.ts"
17import type { Inspectable } from "./Inspectable.ts"
18import { NodeInspectSymbol, toJson } from "./Inspectable.ts"
19import * as Option from "./Option.ts"
20import type { Pipeable } from "./Pipeable.ts"
21import { pipeArguments } from "./Pipeable.ts"
22import { hasProperty } from "./Predicate.ts"
23import type { Result } from "./Result.ts"
24import * as TxRef from "./TxRef.ts"
25
26const TypeId = "~effect/TxHashMap"
27
28const TxHashMapProto = {
29 [TypeId]: TypeId,
30 [NodeInspectSymbol](this: TxHashMap<unknown, unknown>) {
31 return toJson(this)
32 },
33 toString(this: TxHashMap<unknown, unknown>) {
34 return `TxHashMap(${format(toJson((this).ref))})`
35 },
36 toJSON(this: TxHashMap<unknown, unknown>) {
37 return {
38 _id: "TxHashMap",
39 ref: toJson((this).ref)
40 }
41 },
42 pipe(this: TxHashMap<unknown, unknown>) {
43 return pipeArguments(this, arguments)
44 }
45}
46
47/**
48 * A TxHashMap is a transactional hash map data structure that provides atomic operations
49 * on key-value pairs within Effect transactions. It uses an immutable HashMap internally
50 * with TxRef for transactional semantics, ensuring all operations are performed atomically.
51 *
52 * **Example** (Using transactional hash maps)
53 *
54 * ```ts import.meta.vitest
55 * import { Effect, Option, TxHashMap } from "effect"
56 *
57 * const program = Effect.gen(function*() {
58 * // Create a transactional hash map
59 * const txMap = yield* TxHashMap.make(["user1", "Alice"], ["user2", "Bob"])
60 *
61 * // Single operations are automatically transactional
62 * yield* TxHashMap.set(txMap, "user3", "Charlie")
63 * yield* TxHashMap.get(txMap, "user1") // => Option.some("Alice")
64 *
65 * // Multi-step atomic operations
66 * yield* Effect.tx(
67 * Effect.gen(function*() {
68 * const currentUser = yield* TxHashMap.get(txMap, "user1")
69 * if (currentUser._tag === "Some") {
70 * yield* TxHashMap.set(txMap, "user1", currentUser.value + "_updated")
71 * yield* TxHashMap.remove(txMap, "user2")
72 * }
73 * })
74 * )
75 *
76 * return yield* TxHashMap.size(txMap)
77 * })
78 *
79 * await Effect.runPromise(program) // => 2
80 * ```
81 *
82 * @category models
83 * @since 4.0.0
84 */
85export interface TxHashMap<in out K, in out V> extends Inspectable, Pipeable {
86 readonly [TypeId]: typeof TypeId
87 readonly ref: TxRef.TxRef<HashMap.HashMap<K, V>>
88}
89
90/**
91 * The TxHashMap namespace contains type-level utilities and helper types
92 * for working with TxHashMap instances.
93 *
94 * **Example** (Reusing extracted TxHashMap types)
95 *
96 * ```ts import.meta.vitest
97 * import { Effect, Option, TxHashMap } from "effect"
98 *
99 * const program = Effect.gen(function*() {
100 * // Create a transactional inventory map
101 * const inventory = yield* TxHashMap.make(
102 * ["laptop", { stock: 5, price: 999 }],
103 * ["mouse", { stock: 20, price: 29 }]
104 * )
105 *
106 * // Extract types for reuse
107 * type ProductId = TxHashMap.TxHashMap.Key<typeof inventory> // string
108 * type Product = TxHashMap.TxHashMap.Value<typeof inventory> // { stock: number, price: number }
109 * type InventoryEntry = TxHashMap.TxHashMap.Entry<typeof inventory> // [string, Product]
110 *
111 * // Use extracted types in functions
112 * const updateStock = (id: ProductId, newStock: number) =>
113 * TxHashMap.modify(
114 * inventory,
115 * id,
116 * (product) => ({ ...product, stock: newStock })
117 * )
118 *
119 * yield* updateStock("laptop", 3)
120 * return yield* TxHashMap.get(inventory, "laptop")
121 * })
122 *
123 * await Effect.runPromise(program) // => Option.some({ stock: 3, price: 999 })
124 * ```
125 *
126 * @since 4.0.0
127 */
128export declare namespace TxHashMap {
129 /**
130 * Extracts the key type from a TxHashMap type.
131 *
132 * **Example** (Extracting key types)
133 *
134 * ```ts import.meta.vitest
135 * import { Effect, Option, TxHashMap } from "effect"
136 *
137 * const program = Effect.gen(function*() {
138 * // Create a user map to extract key type from
139 * const userMap = yield* TxHashMap.make(
140 * ["alice", { name: "Alice", age: 30 }],
141 * ["bob", { name: "Bob", age: 25 }]
142 * )
143 *
144 * // Extract the key type (string)
145 * type UserKey = TxHashMap.TxHashMap.Key<typeof userMap>
146 *
147 * // Use the extracted type in functions
148 * const getUserById = (id: UserKey) => TxHashMap.get(userMap, id)
149 * return yield* getUserById("alice")
150 * })
151 *
152 * await Effect.runPromise(program) // => Option.some({ name: "Alice", age: 30 })
153 * ```
154 *
155 * @category utility types
156 * @since 4.0.0
157 */
158 export type Key<T extends TxHashMap<any, any>> = T extends TxHashMap<infer K, any> ? K : never
159
160 /**
161 * Extracts the value type from a TxHashMap type.
162 *
163 * **Example** (Extracting value types)
164 *
165 * ```ts import.meta.vitest
166 * import { Effect, Option, TxHashMap } from "effect"
167 *
168 * const program = Effect.gen(function*() {
169 * // Create a product catalog TxHashMap
170 * const catalog = yield* TxHashMap.make(
171 * ["laptop", { price: 999, category: "electronics" }],
172 * ["book", { price: 29, category: "education" }]
173 * )
174 *
175 * // Extract the value type (Product)
176 * type Product = TxHashMap.TxHashMap.Value<typeof catalog>
177 *
178 * // Use the extracted type for type-safe operations
179 * const processProduct = (product: Product) => {
180 * return `${product.category}: $${product.price}`
181 * }
182 *
183 * return Option.map(yield* TxHashMap.get(catalog, "laptop"), processProduct)
184 * })
185 *
186 * await Effect.runPromise(program) // => Option.some("electronics: $999")
187 * ```
188 *
189 * @category utility types
190 * @since 4.0.0
191 */
192 export type Value<T extends TxHashMap<any, any>> = T extends TxHashMap<any, infer V> ? V : never
193
194 /**
195 * Extracts the entry type from a TxHashMap type.
196 *
197 * **Example** (Extracting entry types)
198 *
199 * ```ts import.meta.vitest
200 * import { Effect, TxHashMap } from "effect"
201 *
202 * const program = Effect.gen(function*() {
203 * // Create a configuration TxHashMap
204 * const config = yield* TxHashMap.make(
205 * ["api_url", "https://api.example.com"],
206 * ["timeout", "5000"],
207 * ["retries", "3"]
208 * )
209 *
210 * // Extract the entry type [string, string]
211 * type ConfigEntry = TxHashMap.TxHashMap.Entry<typeof config>
212 *
213 * // Use the extracted type for processing entries
214 * const processEntry = ([key, value]: ConfigEntry) => {
215 * return `${key}=${value}`
216 * }
217 *
218 * // Get all entries and process them
219 * return (yield* TxHashMap.entries(config)).map(processEntry).sort()
220 * })
221 *
222 * await Effect.runPromise(program) // => ["api_url=https://api.example.com", "retries=3", "timeout=5000"]
223 * ```
224 *
225 * @category utility types
226 * @since 4.0.0
227 */
228 export type Entry<T extends TxHashMap<any, any>> = T extends TxHashMap<infer K, infer V> ? readonly [K, V] : never
229}
230
231/**
232 * Creates an empty TxHashMap.
233 *
234 * **Example** (Creating an empty map)
235 *
236 * ```ts import.meta.vitest
237 * import { Effect, TxHashMap } from "effect"
238 *
239 * const program = Effect.gen(function*() {
240 * // Create an empty transactional hash map
241 * const emptyMap = yield* TxHashMap.empty<string, number>()
242 *
243 * // Verify it's empty
244 * yield* TxHashMap.isEmpty(emptyMap) // => true
245 * yield* TxHashMap.size(emptyMap) // => 0
246 *
247 * // Start adding elements
248 * yield* TxHashMap.set(emptyMap, "first", 1)
249 * return yield* TxHashMap.size(emptyMap)
250 * })
251 *
252 * await Effect.runPromise(program) // => 1
253 * ```
254 *
255 * @category constructors
256 * @since 2.0.0
257 */
258export const empty = <K, V>(): Effect.Effect<TxHashMap<K, V>> =>
259 Effect.gen(function*() {
260 const ref = yield* TxRef.make(HashMap.empty<K, V>())
261 return Object.assign(Object.create(TxHashMapProto), { ref })
262 })
263
264/**
265 * Creates a TxHashMap from the provided key-value pairs.
266 *
267 * **Example** (Creating a map from entries)
268 *
269 * ```ts import.meta.vitest
270 * import { Effect, Option, TxHashMap } from "effect"
271 *
272 * const program = Effect.gen(function*() {
273 * // Create a user directory
274 * const userMap = yield* TxHashMap.make(
275 * ["alice", { name: "Alice Smith", role: "admin" }],
276 * ["bob", { name: "Bob Johnson", role: "user" }],
277 * ["charlie", { name: "Charlie Brown", role: "user" }]
278 * )
279 *
280 * // Check the initial size
281 * yield* TxHashMap.size(userMap) // => 3
282 *
283 * // Access users
284 * yield* TxHashMap.get(userMap, "alice") // => Option.some({ name: "Alice Smith", role: "admin" })
285 * return yield* TxHashMap.get(userMap, "david")
286 * })
287 *
288 * await Effect.runPromise(program) // => Option.none()
289 * ```
290 *
291 * @category constructors
292 * @since 2.0.0
293 */
294export const make = <K, V>(
295 ...entries: Array<readonly [K, V]>
296): Effect.Effect<TxHashMap<K, V>> =>
297 Effect.gen(function*() {
298 const hashMap = HashMap.make(...entries)
299 const ref = yield* TxRef.make(hashMap)
300 return Object.assign(Object.create(TxHashMapProto), { ref })
301 })
302
303/**
304 * Creates a TxHashMap from an iterable of key-value pairs.
305 *
306 * **Example** (Creating a map from an iterable)
307 *
308 * ```ts import.meta.vitest
309 * import { Effect, Option, TxHashMap } from "effect"
310 *
311 * const program = Effect.gen(function*() {
312 * // Create from various iterable sources
313 * const configEntries = [
314 * ["database.host", "localhost"],
315 * ["database.port", "5432"],
316 * ["cache.enabled", "true"],
317 * ["logging.level", "info"]
318 * ] as const
319 *
320 * const configMap = yield* TxHashMap.fromIterable(configEntries)
321 *
322 * // Verify the configuration was loaded
323 * yield* TxHashMap.size(configMap) // => 4
324 * yield* TxHashMap.get(configMap, "database.host") // => Option.some("localhost")
325 *
326 * // Can also create from Map, Set of tuples, etc.
327 * const jsMap = new Map([["key1", "value1"], ["key2", "value2"]])
328 * return yield* TxHashMap.fromIterable(jsMap)
329 * })
330 *
331 * await Effect.runPromise(program)
332 * ```
333 *
334 * @category constructors
335 * @since 2.0.0
336 */
337export const fromIterable = <K, V>(
338 entries: Iterable<readonly [K, V]>
339): Effect.Effect<TxHashMap<K, V>> =>
340 Effect.gen(function*() {
341 const hashMap = HashMap.fromIterable(entries)
342 const ref = yield* TxRef.make(hashMap)
343 return Object.assign(Object.create(TxHashMapProto), { ref })
344 })
345
346/**
347 * Looks up the value for the specified key in the TxHashMap.
348 *
349 * **Example** (Looking up values safely)
350 *
351 * ```ts import.meta.vitest
352 * import { Effect, Option, TxHashMap } from "effect"
353 *
354 * const program = Effect.gen(function*() {
355 * const userMap = yield* TxHashMap.make(
356 * ["alice", { name: "Alice", role: "admin" }],
357 * ["bob", { name: "Bob", role: "user" }]
358 * )
359 *
360 * // Safe lookup - returns Option
361 * yield* TxHashMap.get(userMap, "alice") // => Option.some({ name: "Alice", role: "admin" })
362 * yield* TxHashMap.get(userMap, "charlie") // => Option.none()
363 *
364 * // Use with pipe syntax for type-safe access
365 * return yield* TxHashMap.get(userMap, "bob")
366 * })
367 *
368 * await Effect.runPromise(program) // => Option.some({ name: "Bob", role: "user" })
369 * ```
370 *
371 * @category combinators
372 * @since 2.0.0
373 */
374export const get: {
375 <K1 extends K, K>(key: K1): <V>(self: TxHashMap<K, V>) => Effect.Effect<Option.Option<V>>
376 <K1 extends K, K, V>(self: TxHashMap<K, V>, key: K1): Effect.Effect<Option.Option<V>>
377} = dual(
378 2,
379 <K1 extends K, K, V>(self: TxHashMap<K, V>, key: K1): Effect.Effect<Option.Option<V>> =>
380 Effect.gen(function*() {
381 const map = yield* TxRef.get(self.ref)
382 return HashMap.get(map, key)
383 })
384)
385
386/**
387 * Sets the value for the specified key in the TxHashMap.
388 *
389 * **Details**
390 *
391 * This function mutates the original TxHashMap by updating its internal state.
392 * It does not return a new TxHashMap reference.
393 *
394 * **Example** (Setting values)
395 *
396 * ```ts import.meta.vitest
397 * import { Effect, Option, TxHashMap } from "effect"
398 *
399 * const program = Effect.gen(function*() {
400 * const inventory = yield* TxHashMap.make(
401 * ["laptop", 5],
402 * ["mouse", 20]
403 * )
404 *
405 * // Update existing item
406 * yield* TxHashMap.set(inventory, "laptop", 3)
407 * yield* TxHashMap.get(inventory, "laptop") // => Option.some(3)
408 *
409 * // Add new item
410 * yield* TxHashMap.set(inventory, "keyboard", 15)
411 * yield* TxHashMap.get(inventory, "keyboard") // => Option.some(15)
412 *
413 * // Use with pipe syntax
414 * yield* TxHashMap.set("tablet", 8)(inventory)
415 * return yield* TxHashMap.get(inventory, "tablet")
416 * })
417 *
418 * await Effect.runPromise(program) // => Option.some(8)
419 * ```
420 *
421 * @category combinators
422 * @since 2.0.0
423 */
424export const set: {
425 <K, V>(key: K, value: V): (self: TxHashMap<K, V>) => Effect.Effect<void>
426 <K, V>(self: TxHashMap<K, V>, key: K, value: V): Effect.Effect<void>
427} = dual(
428 3,
429 <K, V>(self: TxHashMap<K, V>, key: K, value: V): Effect.Effect<void> =>
430 TxRef.update(self.ref, (map) => HashMap.set(map, key, value))
431)
432
433/**
434 * Checks whether the specified key exists in the TxHashMap.
435 *
436 * **Example** (Checking for keys)
437 *
438 * ```ts import.meta.vitest
439 * import { Effect, TxHashMap } from "effect"
440 *
441 * const program = Effect.gen(function*() {
442 * const permissions = yield* TxHashMap.make(
443 * ["alice", ["read", "write"]],
444 * ["bob", ["read"]],
445 * ["charlie", ["admin"]]
446 * )
447 *
448 * // Check if users exist
449 * yield* TxHashMap.has(permissions, "alice") // => true
450 * yield* TxHashMap.has(permissions, "david") // => false
451 *
452 * // Use direct method call for type-safe access
453 * return yield* TxHashMap.has(permissions, "bob")
454 * })
455 *
456 * await Effect.runPromise(program) // => true
457 * ```
458 *
459 * @category predicates
460 * @since 2.0.0
461 */
462export const has: {
463 <K1 extends K, K>(key: K1): <V>(self: TxHashMap<K, V>) => Effect.Effect<boolean>
464 <K1 extends K, K, V>(self: TxHashMap<K, V>, key: K1): Effect.Effect<boolean>
465} = dual(
466 2,
467 <K1 extends K, K, V>(self: TxHashMap<K, V>, key: K1): Effect.Effect<boolean> =>
468 Effect.gen(function*() {
469 const map = yield* TxRef.get(self.ref)
470 return HashMap.has(map, key)
471 })
472)
473
474/**
475 * Removes the specified key from the TxHashMap.
476 *
477 * **Details**
478 *
479 * This function mutates the original TxHashMap by removing the specified
480 * key-value pair. It does not return a new TxHashMap reference.
481 *
482 * **Example** (Removing keys)
483 *
484 * ```ts import.meta.vitest
485 * import { Effect, TxHashMap } from "effect"
486 *
487 * const program = Effect.gen(function*() {
488 * const cache = yield* TxHashMap.make(
489 * ["user:1", { name: "Alice", lastSeen: "2024-01-01" }],
490 * ["user:2", { name: "Bob", lastSeen: "2024-01-02" }],
491 * ["user:3", { name: "Charlie", lastSeen: "2023-12-30" }]
492 * )
493 *
494 * // Remove expired user
495 * yield* TxHashMap.remove(cache, "user:3") // => true
496 *
497 * // Try to remove non-existent key
498 * yield* TxHashMap.remove(cache, "user:999") // => false
499 *
500 * // Verify removal
501 * yield* TxHashMap.has(cache, "user:3") // => false
502 * return yield* TxHashMap.size(cache)
503 * })
504 *
505 * await Effect.runPromise(program) // => 2
506 * ```
507 *
508 * @category combinators
509 * @since 2.0.0
510 */
511export const remove: {
512 <K1 extends K, K>(key: K1): <V>(self: TxHashMap<K, V>) => Effect.Effect<boolean>
513 <K1 extends K, K, V>(self: TxHashMap<K, V>, key: K1): Effect.Effect<boolean>
514} = dual(
515 2,
516 <K1 extends K, K, V>(self: TxHashMap<K, V>, key: K1): Effect.Effect<boolean> =>
517 Effect.gen(function*() {
518 const currentMap = yield* TxRef.get(self.ref)
519 const existed = HashMap.has(currentMap, key)
520 if (existed) {
521 yield* TxRef.set(self.ref, HashMap.remove(currentMap, key))
522 }
523 return existed
524 }).pipe(Effect.tx)
525)
526
527/**
528 * Removes all entries from the TxHashMap.
529 *
530 * **Details**
531 *
532 * This function mutates the original TxHashMap by clearing all key-value pairs.
533 * It does not return a new TxHashMap reference.
534 *
535 * **Example** (Clearing all entries)
536 *
537 * ```ts import.meta.vitest
538 * import { Effect, TxHashMap } from "effect"
539 *
540 * const program = Effect.gen(function*() {
541 * const sessionMap = yield* TxHashMap.make(
542 * ["session1", { userId: "alice", expires: "2024-01-01T12:00:00Z" }],
543 * ["session2", { userId: "bob", expires: "2024-01-01T13:00:00Z" }],
544 * ["session3", { userId: "charlie", expires: "2024-01-01T14:00:00Z" }]
545 * )
546 *
547 * // Check initial state
548 * yield* TxHashMap.size(sessionMap) // => 3
549 *
550 * // Clear all sessions (e.g., during maintenance)
551 * yield* TxHashMap.clear(sessionMap)
552 *
553 * // Verify cleared
554 * yield* TxHashMap.size(sessionMap) // => 0
555 * return yield* TxHashMap.isEmpty(sessionMap)
556 * })
557 *
558 * await Effect.runPromise(program) // => true
559 * ```
560 *
561 * @category combinators
562 * @since 4.0.0
563 */
564export const clear = <K, V>(self: TxHashMap<K, V>): Effect.Effect<void> => TxRef.set(self.ref, HashMap.empty<K, V>())
565
566/**
567 * Returns the number of entries in the TxHashMap.
568 *
569 * **Example** (Counting entries)
570 *
571 * ```ts import.meta.vitest
572 * import { Effect, TxHashMap } from "effect"
573 *
574 * const program = Effect.gen(function*() {
575 * const metrics = yield* TxHashMap.make(
576 * ["requests", 1000],
577 * ["errors", 5],
578 * ["users", 50]
579 * )
580 *
581 * yield* TxHashMap.size(metrics) // => 3
582 *
583 * // Add more metrics
584 * yield* TxHashMap.set(metrics, "response_time", 250)
585 * yield* TxHashMap.size(metrics) // => 4
586 *
587 * // Remove a metric
588 * yield* TxHashMap.remove(metrics, "errors")
589 * return yield* TxHashMap.size(metrics)
590 * })
591 *
592 * await Effect.runPromise(program) // => 3
593 * ```
594 *
595 * @category combinators
596 * @since 2.0.0
597 */
598export const size = <K, V>(self: TxHashMap<K, V>): Effect.Effect<number> =>
599 Effect.gen(function*() {
600 const map = yield* TxRef.get(self.ref)
601 return HashMap.size(map)
602 })
603
604/**
605 * Checks whether the TxHashMap is empty.
606 *
607 * **Example** (Checking for an empty map)
608 *
609 * ```ts import.meta.vitest
610 * import { Effect, TxHashMap } from "effect"
611 *
612 * const program = Effect.gen(function*() {
613 * // Start with empty map
614 * const cache = yield* TxHashMap.empty<string, any>()
615 * yield* TxHashMap.isEmpty(cache) // => true
616 *
617 * // Add an item
618 * yield* TxHashMap.set(cache, "key1", "value1")
619 * yield* TxHashMap.isEmpty(cache) // => false
620 *
621 * // Clear and check again
622 * yield* TxHashMap.clear(cache)
623 * return yield* TxHashMap.isEmpty(cache)
624 * })
625 *
626 * await Effect.runPromise(program) // => true
627 * ```
628 *
629 * @category predicates
630 * @since 2.0.0
631 */
632export const isEmpty = <K, V>(self: TxHashMap<K, V>): Effect.Effect<boolean> =>
633 Effect.gen(function*() {
634 const map = yield* TxRef.get(self.ref)
635 return HashMap.isEmpty(map)
636 })
637
638/**
639 * Checks whether the TxHashMap is non-empty.
640 *
641 * **Example** (Checking for a non-empty map)
642 *
643 * ```ts import.meta.vitest
644 * import { Effect, TxHashMap } from "effect"
645 *
646 * const program = Effect.gen(function*() {
647 * const inventory = yield* TxHashMap.make(["laptop", 5])
648 *
649 * yield* TxHashMap.isNonEmpty(inventory) // => true
650 *
651 * // Clear inventory
652 * yield* TxHashMap.clear(inventory)
653 * return yield* TxHashMap.isNonEmpty(inventory)
654 * })
655 *
656 * await Effect.runPromise(program) // => false
657 * ```
658 *
659 * @category predicates
660 * @since 4.0.0
661 */
662export const isNonEmpty = <K, V>(self: TxHashMap<K, V>): Effect.Effect<boolean> =>
663 Effect.map(isEmpty(self), (empty) => !empty)
664
665/**
666 * Updates the value for the specified key if it exists, returning the previous value in `Some`; returns `None` and leaves the map unchanged when the key is absent.
667 *
668 * **Details**
669 *
670 * This function mutates the original TxHashMap by updating the value at the
671 * specified key. It does not return a new TxHashMap reference.
672 *
673 * **Example** (Updating existing values)
674 *
675 * ```ts import.meta.vitest
676 * import { Effect, Option, TxHashMap } from "effect"
677 *
678 * const program = Effect.gen(function*() {
679 * const counters = yield* TxHashMap.make(
680 * ["downloads", 100],
681 * ["views", 250]
682 * )
683 *
684 * // Increment existing counter
685 * const oldDownloads = yield* TxHashMap.modify(
686 * counters,
687 * "downloads",
688 * (count) => count + 1
689 * )
690 * oldDownloads // => Option.some(100)
691 *
692 * yield* TxHashMap.get(counters, "downloads") // => Option.some(101)
693 *
694 * // Try to modify non-existent key
695 * const nonExistent = yield* TxHashMap.modify(
696 * counters,
697 * "clicks",
698 * (count) => count + 1
699 * )
700 * nonExistent // => Option.none()
701 *
702 * // Update views counter with direct method call
703 * yield* TxHashMap.modify(counters, "views", (views) => views * 2)
704 * return yield* TxHashMap.get(counters, "views")
705 * })
706 *
707 * await Effect.runPromise(program) // => Option.some(500)
708 * ```
709 *
710 * @category combinators
711 * @since 4.0.0
712 */
713export const modify: {
714 <K, V>(
715 key: K,
716 f: (value: V) => V
717 ): (self: TxHashMap<K, V>) => Effect.Effect<Option.Option<V>>
718 <K, V>(self: TxHashMap<K, V>, key: K, f: (value: V) => V): Effect.Effect<Option.Option<V>>
719} = dual(
720 3,
721 <K, V>(
722 self: TxHashMap<K, V>,
723 key: K,
724 f: (value: V) => V
725 ): Effect.Effect<Option.Option<V>> =>
726 Effect.gen(function*() {
727 const currentMap = yield* TxRef.get(self.ref)
728 const currentValue = HashMap.get(currentMap, key)
729 if (Option.isSome(currentValue)) {
730 const newValue = f(currentValue.value)
731 yield* TxRef.set(self.ref, HashMap.set(currentMap, key, newValue))
732 return currentValue
733 }
734 return Option.none()
735 }).pipe(Effect.tx)
736)
737
738/**
739 * Updates the value for the specified key using an Option-based update function.
740 *
741 * **Details**
742 *
743 * This function mutates the original TxHashMap by updating, adding, or removing
744 * the key-value pair based on the function result. It does not return a new
745 * TxHashMap reference.
746 *
747 * **Example** (Updating values with Option)
748 *
749 * ```ts import.meta.vitest
750 * import { Effect, Option, TxHashMap } from "effect"
751 *
752 * const program = Effect.gen(function*() {
753 * const storage = yield* TxHashMap.make<string, string | number>([
754 * "file1.txt",
755 * "content1"
756 * ], ["access_count", 0])
757 * const increment = Option.map((value: string | number) => typeof value === "number" ? value + 1 : value)
758 *
759 * // Increment existing counter
760 * yield* TxHashMap.modifyAt(storage, "access_count", increment)
761 * yield* TxHashMap.get(storage, "access_count") // => Option.some(1)
762 *
763 * // Increment existing counter again
764 * yield* TxHashMap.modifyAt(storage, "access_count", increment)
765 * yield* TxHashMap.get(storage, "access_count") // => Option.some(2)
766 *
767 * // Update an existing string entry
768 * yield* TxHashMap.modifyAt(
769 * storage,
770 * "file1.txt",
771 * Option.map((value) => typeof value === "string" ? `${value}.bak` : value)
772 * )
773 * return yield* TxHashMap.get(storage, "file1.txt")
774 * })
775 *
776 * await Effect.runPromise(program) // => Option.some("content1.bak")
777 * ```
778 *
779 * @category combinators
780 * @since 4.0.0
781 */
782export const modifyAt: {
783 <K, V>(
784 key: K,
785 f: (value: Option.Option<V>) => Option.Option<V>
786 ): (self: TxHashMap<K, V>) => Effect.Effect<void>
787 <K, V>(
788 self: TxHashMap<K, V>,
789 key: K,
790 f: (value: Option.Option<V>) => Option.Option<V>
791 ): Effect.Effect<void>
792} = dual(
793 3,
794 <K, V>(
795 self: TxHashMap<K, V>,
796 key: K,
797 f: (value: Option.Option<V>) => Option.Option<V>
798 ): Effect.Effect<void> =>
799 Effect.gen(function*() {
800 const currentMap = yield* TxRef.get(self.ref)
801 const currentValue = HashMap.get(currentMap, key)
802 const newValue = f(currentValue)
803
804 if (Option.isSome(newValue)) {
805 yield* TxRef.set(self.ref, HashMap.set(currentMap, key, newValue.value))
806 } else if (Option.isSome(currentValue)) {
807 yield* TxRef.set(self.ref, HashMap.remove(currentMap, key))
808 }
809 }).pipe(Effect.tx)
810)
811
812/**
813 * Returns an array of all keys in the TxHashMap.
814 *
815 * **Example** (Reading keys)
816 *
817 * ```ts import.meta.vitest
818 * import { Effect, Option, TxHashMap } from "effect"
819 *
820 * const program = Effect.gen(function*() {
821 * const userRoles = yield* TxHashMap.make(
822 * ["alice", "admin"],
823 * ["bob", "user"],
824 * ["charlie", "moderator"]
825 * )
826 *
827 * const usernames = (yield* TxHashMap.keys(userRoles)).sort()
828 * usernames // => ["alice", "bob", "charlie"]
829 *
830 * // Useful for iteration
831 * const assignments: Array<string> = []
832 * for (const username of usernames) {
833 * const role = yield* TxHashMap.get(userRoles, username)
834 * if (role._tag === "Some") {
835 * assignments.push(`${username}: ${role.value}`)
836 * }
837 * }
838 * return assignments
839 * })
840 *
841 * await Effect.runPromise(program) // => ["alice: admin", "bob: user", "charlie: moderator"]
842 * ```
843 *
844 * @category combinators
845 * @since 2.0.0
846 */
847export const keys = <K, V>(self: TxHashMap<K, V>): Effect.Effect<Array<K>> =>
848 Effect.gen(function*() {
849 const map = yield* TxRef.get(self.ref)
850 return Array.from(HashMap.keys(map))
851 })
852
853/**
854 * Returns an array of all values in the TxHashMap.
855 *
856 * **Example** (Reading values)
857 *
858 * ```ts import.meta.vitest
859 * import { Effect, TxHashMap } from "effect"
860 *
861 * const program = Effect.gen(function*() {
862 * const scores = yield* TxHashMap.make(
863 * ["alice", 95],
864 * ["bob", 87],
865 * ["charlie", 92]
866 * )
867 *
868 * const allScores = (yield* TxHashMap.values(scores)).sort((a, b) => a - b)
869 * allScores // => [87, 92, 95]
870 *
871 * // Calculate average
872 * const average = allScores.reduce((sum, score) => sum + score, 0) /
873 * allScores.length
874 * average.toFixed(2) // => "91.33"
875 *
876 * // Find maximum
877 * return Math.max(...allScores)
878 * })
879 *
880 * await Effect.runPromise(program) // => 95
881 * ```
882 *
883 * @category combinators
884 * @since 2.0.0
885 */
886export const values = <K, V>(self: TxHashMap<K, V>): Effect.Effect<Array<V>> =>
887 Effect.gen(function*() {
888 const map = yield* TxRef.get(self.ref)
889 return HashMap.toValues(map)
890 })
891
892/**
893 * Returns an array of all key-value pairs in the TxHashMap.
894 *
895 * **Example** (Reading entries)
896 *
897 * ```ts import.meta.vitest
898 * import { Effect, TxHashMap } from "effect"
899 *
900 * const program = Effect.gen(function*() {
901 * const config = yield* TxHashMap.make(
902 * ["host", "localhost"],
903 * ["port", "3000"],
904 * ["ssl", "false"]
905 * )
906 *
907 * return (yield* TxHashMap.entries(config)).toSorted(([left], [right]) => left.localeCompare(right))
908 * })
909 *
910 * await Effect.runPromise(program) // => [["host", "localhost"], ["port", "3000"], ["ssl", "false"]]
911 * ```
912 *
913 * @category combinators
914 * @since 4.0.0
915 */
916export const entries = <K, V>(
917 self: TxHashMap<K, V>
918): Effect.Effect<Array<readonly [K, V]>> =>
919 Effect.gen(function*() {
920 const map = yield* TxRef.get(self.ref)
921 return HashMap.toEntries(map)
922 })
923
924/**
925 * Returns an immutable snapshot of the current TxHashMap state.
926 *
927 * **Example** (Taking immutable snapshots)
928 *
929 * ```ts import.meta.vitest
930 * import { Effect, HashMap, Option, TxHashMap } from "effect"
931 *
932 * const program = Effect.gen(function*() {
933 * const liveData = yield* TxHashMap.make(
934 * ["temperature", 22.5],
935 * ["humidity", 45.2],
936 * ["pressure", 1013.25]
937 * )
938 *
939 * // Take snapshot for reporting
940 * const snapshot = yield* TxHashMap.snapshot(liveData)
941 *
942 * // Continue modifying live data
943 * yield* TxHashMap.set(liveData, "temperature", 23.1)
944 * yield* TxHashMap.set(liveData, "wind_speed", 5.3)
945 *
946 * // Snapshot remains unchanged
947 * HashMap.size(snapshot) // => 3
948 * HashMap.get(snapshot, "temperature") // => Option.some(22.5)
949 *
950 * // Can use regular HashMap operations on snapshot
951 * return HashMap.get(snapshot, "humidity")
952 * })
953 *
954 * await Effect.runPromise(program) // => Option.some(45.2)
955 * ```
956 *
957 * @category combinators
958 * @since 4.0.0
959 */
960export const snapshot = <K, V>(
961 self: TxHashMap<K, V>
962): Effect.Effect<HashMap.HashMap<K, V>> => TxRef.get(self.ref)
963
964/**
965 * Merges another HashMap into this TxHashMap. If both maps contain the same key,
966 * the value from the other map will be used.
967 *
968 * **Details**
969 *
970 * This function mutates the original TxHashMap by merging the provided HashMap
971 * into it. It does not return a new TxHashMap reference.
972 *
973 * **Example** (Merging HashMaps)
974 *
975 * ```ts import.meta.vitest
976 * import { Effect, HashMap, Option, TxHashMap } from "effect"
977 *
978 * const program = Effect.gen(function*() {
979 * // Create initial user preferences
980 * const userPrefs = yield* TxHashMap.make(
981 * ["theme", "light"],
982 * ["language", "en"],
983 * ["notifications", "enabled"]
984 * )
985 *
986 * // New preferences to merge in
987 * const newSettings = HashMap.make(
988 * ["theme", "dark"], // will override existing
989 * ["timezone", "UTC"], // new setting
990 * ["sound", "enabled"] // new setting
991 * )
992 *
993 * // Merge the new settings
994 * yield* TxHashMap.union(userPrefs, newSettings)
995 *
996 * // Check the merged result
997 * yield* TxHashMap.get(userPrefs, "theme") // => Option.some("dark")
998 * yield* TxHashMap.get(userPrefs, "language") // => Option.some("en")
999 * yield* TxHashMap.get(userPrefs, "timezone") // => Option.some("UTC")
1000 * return yield* TxHashMap.size(userPrefs)
1001 * })
1002 *
1003 * await Effect.runPromise(program) // => 5
1004 * ```
1005 *
1006 * @category combinators
1007 * @since 4.0.0
1008 */
1009export const union: {
1010 <K1 extends K, K, V1 extends V, V>(
1011 other: HashMap.HashMap<K1, V1>
1012 ): (self: TxHashMap<K, V>) => Effect.Effect<void>
1013 <K1 extends K, K, V1 extends V, V>(
1014 self: TxHashMap<K, V>,
1015 other: HashMap.HashMap<K1, V1>
1016 ): Effect.Effect<void>
1017} = dual(
1018 2,
1019 <K1 extends K, K, V1 extends V, V>(
1020 self: TxHashMap<K, V>,
1021 other: HashMap.HashMap<K1, V1>
1022 ): Effect.Effect<void> => TxRef.update(self.ref, (map) => HashMap.union(map, other))
1023)
1024
1025/**
1026 * Removes multiple keys from the TxHashMap.
1027 *
1028 * **Details**
1029 *
1030 * This function mutates the original TxHashMap by removing all specified keys.
1031 * It does not return a new TxHashMap reference.
1032 *
1033 * **Example** (Removing multiple keys)
1034 *
1035 * ```ts import.meta.vitest
1036 * import { Effect, Option, TxHashMap } from "effect"
1037 *
1038 * const program = Effect.gen(function*() {
1039 * // Create a cache with temporary data
1040 * const cache = yield* TxHashMap.make(
1041 * ["session_1", { user: "alice", expires: "2024-01-01" }],
1042 * ["session_2", { user: "bob", expires: "2024-01-01" }],
1043 * ["session_3", { user: "charlie", expires: "2024-12-31" }],
1044 * ["temp_data_1", { value: "temporary" }],
1045 * ["temp_data_2", { value: "also_temporary" }]
1046 * )
1047 *
1048 * yield* TxHashMap.size(cache) // => 5
1049 *
1050 * // Remove expired sessions and temporary data
1051 * const keysToRemove = ["session_1", "session_2", "temp_data_1", "temp_data_2"]
1052 * yield* TxHashMap.removeMany(cache, keysToRemove)
1053 *
1054 * yield* TxHashMap.size(cache) // => 1
1055 *
1056 * // Verify only the valid session remains
1057 * yield* TxHashMap.get(cache, "session_3") // => Option.some({ user: "charlie", expires: "2024-12-31" })
1058 *
1059 * // Can also remove from Set, Array, or any iterable
1060 * const moreKeysToRemove = new Set(["session_3"])
1061 * yield* TxHashMap.removeMany(cache, moreKeysToRemove)
1062 * return yield* TxHashMap.isEmpty(cache)
1063 * })
1064 *
1065 * await Effect.runPromise(program) // => true
1066 * ```
1067 *
1068 * @category combinators
1069 * @since 4.0.0
1070 */
1071export const removeMany: {
1072 <K1 extends K, K>(keys: Iterable<K1>): <V>(self: TxHashMap<K, V>) => Effect.Effect<void>
1073 <K1 extends K, K, V>(self: TxHashMap<K, V>, keys: Iterable<K1>): Effect.Effect<void>
1074} = dual(
1075 2,
1076 <K1 extends K, K, V>(self: TxHashMap<K, V>, keys: Iterable<K1>): Effect.Effect<void> =>
1077 TxRef.update(self.ref, (map) => HashMap.removeMany(map, keys))
1078)
1079
1080/**
1081 * Sets multiple key-value pairs in the TxHashMap.
1082 *
1083 * **Details**
1084 *
1085 * This function mutates the original TxHashMap by setting all provided key-value
1086 * pairs. It does not return a new TxHashMap reference.
1087 *
1088 * **Example** (Setting multiple entries)
1089 *
1090 * ```ts import.meta.vitest
1091 * import { Effect, Option, TxHashMap } from "effect"
1092 *
1093 * const program = Effect.gen(function*() {
1094 * // Create an empty product catalog
1095 * const catalog = yield* TxHashMap.empty<
1096 * string,
1097 * { price: number; stock: number }
1098 * >()
1099 *
1100 * // Bulk load initial products
1101 * const initialProducts: Array<
1102 * readonly [string, { price: number; stock: number }]
1103 * > = [
1104 * ["laptop", { price: 999, stock: 5 }],
1105 * ["mouse", { price: 29, stock: 50 }],
1106 * ["keyboard", { price: 79, stock: 20 }],
1107 * ["monitor", { price: 299, stock: 8 }]
1108 * ]
1109 *
1110 * yield* TxHashMap.setMany(catalog, initialProducts)
1111 *
1112 * yield* TxHashMap.size(catalog) // => 4
1113 *
1114 * // Update prices with a new batch
1115 * const priceUpdates: Array<
1116 * readonly [string, { price: number; stock: number }]
1117 * > = [
1118 * ["laptop", { price: 899, stock: 5 }], // sale price
1119 * ["mouse", { price: 25, stock: 50 }], // sale price
1120 * ["webcam", { price: 89, stock: 12 }] // new product
1121 * ]
1122 *
1123 * yield* TxHashMap.setMany(catalog, priceUpdates)
1124 *
1125 * yield* TxHashMap.size(catalog) // => 5
1126 *
1127 * // Verify the updates
1128 * yield* TxHashMap.get(catalog, "laptop") // => Option.some({ price: 899, stock: 5 })
1129 *
1130 * // Can also use Map, Set of tuples, or any iterable of entries
1131 * const jsMap = new Map([["tablet", { price: 399, stock: 3 }]])
1132 * yield* TxHashMap.setMany(catalog, jsMap)
1133 * return yield* TxHashMap.get(catalog, "tablet")
1134 * })
1135 *
1136 * await Effect.runPromise(program) // => Option.some({ price: 399, stock: 3 })
1137 * ```
1138 *
1139 * @category combinators
1140 * @since 4.0.0
1141 */
1142export const setMany: {
1143 <K1 extends K, K, V1 extends V, V>(
1144 entries: Iterable<readonly [K1, V1]>
1145 ): (self: TxHashMap<K, V>) => Effect.Effect<void>
1146 <K1 extends K, K, V1 extends V, V>(
1147 self: TxHashMap<K, V>,
1148 entries: Iterable<readonly [K1, V1]>
1149 ): Effect.Effect<void>
1150} = dual(
1151 2,
1152 <K1 extends K, K, V1 extends V, V>(
1153 self: TxHashMap<K, V>,
1154 entries: Iterable<readonly [K1, V1]>
1155 ): Effect.Effect<void> => TxRef.update(self.ref, (map) => HashMap.setMany(map, entries))
1156)
1157
1158/**
1159 * Returns `true` if the specified value is a `TxHashMap`, `false` otherwise.
1160 *
1161 * **Example** (Checking TxHashMap values)
1162 *
1163 * ```ts import.meta.vitest
1164 * import { Effect, Exit, TxHashMap } from "effect"
1165 *
1166 * const program = Effect.gen(function*() {
1167 * const txMap = yield* TxHashMap.make(["key", "value"])
1168 *
1169 * TxHashMap.isTxHashMap(txMap) // => true
1170 * TxHashMap.isTxHashMap({}) // => false
1171 * TxHashMap.isTxHashMap(null) // => false
1172 * TxHashMap.isTxHashMap("not a map") // => false
1173 *
1174 * // Useful for type guards in runtime checks
1175 * const validateInput = (value: unknown) => {
1176 * if (TxHashMap.isTxHashMap(value)) {
1177 * // TypeScript now knows this is a TxHashMap
1178 * return Effect.succeed("Valid TxHashMap")
1179 * }
1180 * return Effect.fail("Invalid input")
1181 * }
1182 *
1183 * yield* Effect.exit(validateInput(null)) // => Exit.fail("Invalid input")
1184 * return yield* Effect.exit(validateInput(txMap))
1185 * })
1186 *
1187 * await Effect.runPromise(program) // => Exit.succeed("Valid TxHashMap")
1188 * ```
1189 *
1190 * @category guards
1191 * @since 4.0.0
1192 */
1193export const isTxHashMap = <K, V>(value: unknown): value is TxHashMap<K, V> => {
1194 return hasProperty(value, TypeId)
1195}
1196
1197/**
1198 * Looks up the value for the specified key using a caller-supplied hash.
1199 *
1200 * **Gotchas**
1201 *
1202 * The supplied hash must be the hash for the same key, such as a precomputed
1203 * `Hash.hash(key)` value. If the hash does not match the key, an existing entry
1204 * may not be found.
1205 *
1206 * **Example** (Looking up values with precomputed hashes)
1207 *
1208 * ```ts import.meta.vitest
1209 * import { Effect, Hash, Option, TxHashMap } from "effect"
1210 *
1211 * const program = Effect.gen(function*() {
1212 * // Create a cache with user sessions
1213 * const cache = yield* TxHashMap.make(
1214 * ["session_abc123", { userId: "user1", lastActive: 1_700_000_000_000 }],
1215 * ["session_def456", { userId: "user2", lastActive: 1_700_000_060_000 }]
1216 * )
1217 *
1218 * // When you have precomputed hash (e.g., from another lookup)
1219 * const sessionId = "session_abc123"
1220 * const precomputedHash = Hash.string(sessionId)
1221 *
1222 * // Use hash-optimized lookup for performance in hot paths
1223 * const session = yield* TxHashMap.getHash(cache, sessionId, precomputedHash)
1224 * session // => Option.some({ userId: "user1", lastActive: 1_700_000_000_000 })
1225 *
1226 * // This avoids recomputing the hash when you already have it
1227 * return yield* TxHashMap.getHash(
1228 * cache,
1229 * "invalid",
1230 * Hash.string("invalid")
1231 * )
1232 * })
1233 *
1234 * await Effect.runPromise(program) // => Option.none()
1235 * ```
1236 *
1237 * @category combinators
1238 * @since 4.0.0
1239 */
1240export const getHash: {
1241 <K1 extends K, K>(
1242 key: K1,
1243 hash: number
1244 ): <V>(self: TxHashMap<K, V>) => Effect.Effect<Option.Option<V>>
1245 <K1 extends K, K, V>(
1246 self: TxHashMap<K, V>,
1247 key: K1,
1248 hash: number
1249 ): Effect.Effect<Option.Option<V>>
1250} = dual(
1251 3,
1252 <K1 extends K, K, V>(
1253 self: TxHashMap<K, V>,
1254 key: K1,
1255 hash: number
1256 ): Effect.Effect<Option.Option<V>> => TxRef.get(self.ref).pipe(Effect.map((map) => HashMap.getHash(map, key, hash)))
1257)
1258
1259/**
1260 * Checks whether the specified key has an entry using a caller-supplied hash.
1261 *
1262 * **Gotchas**
1263 *
1264 * The supplied hash must be the hash for the same key, such as a precomputed
1265 * `Hash.hash(key)` value. If the hash does not match the key, an existing entry
1266 * may not be found.
1267 *
1268 * **Example** (Checking keys with precomputed hashes)
1269 *
1270 * ```ts import.meta.vitest
1271 * import { Effect, Hash, TxHashMap } from "effect"
1272 *
1273 * const program = Effect.gen(function*() {
1274 * // Create an access control map
1275 * const permissions = yield* TxHashMap.make(
1276 * ["admin", { read: true, write: true, delete: true }],
1277 * ["user", { read: true, write: false, delete: false }]
1278 * )
1279 *
1280 * // When checking permissions frequently with same roles
1281 * const role = "admin"
1282 * const roleHash = Hash.string(role)
1283 *
1284 * // Use hash-optimized existence check
1285 * yield* TxHashMap.hasHash(permissions, role, roleHash) // => true
1286 *
1287 * // Check non-existent role
1288 * yield* TxHashMap.hasHash(
1289 * permissions,
1290 * "guest",
1291 * Hash.string("guest")
1292 * ) // => false
1293 *
1294 * // Useful in hot paths where hash is computed once and reused
1295 * const roles = ["admin", "user", "moderator"]
1296 * const roleHashes = roles.map((role) => [role, Hash.string(role)] as const)
1297 * const results: Array<string> = []
1298 * for (const [role, hash] of roleHashes) {
1299 * const exists = yield* TxHashMap.hasHash(permissions, role, hash)
1300 * results.push(`Role ${role}: ${exists}`)
1301 * }
1302 * return results
1303 * })
1304 *
1305 * await Effect.runPromise(program) // => ["Role admin: true", "Role user: true", "Role moderator: false"]
1306 * ```
1307 *
1308 * @category predicates
1309 * @since 4.0.0
1310 */
1311export const hasHash: {
1312 <K1 extends K, K>(
1313 key: K1,
1314 hash: number
1315 ): <V>(self: TxHashMap<K, V>) => Effect.Effect<boolean>
1316 <K1 extends K, K, V>(self: TxHashMap<K, V>, key: K1, hash: number): Effect.Effect<boolean>
1317} = dual(
1318 3,
1319 <K1 extends K, K, V>(
1320 self: TxHashMap<K, V>,
1321 key: K1,
1322 hash: number
1323 ): Effect.Effect<boolean> => TxRef.get(self.ref).pipe(Effect.map((map) => HashMap.hasHash(map, key, hash)))
1324)
1325
1326/**
1327 * Transforms all values in the TxHashMap using the provided function, preserving keys.
1328 *
1329 * **Details**
1330 *
1331 * This function returns a new TxHashMap reference with the transformed values.
1332 * The original TxHashMap is not modified.
1333 *
1334 * **Example** (Mapping values)
1335 *
1336 * ```ts import.meta.vitest
1337 * import { Effect, Option, TxHashMap } from "effect"
1338 *
1339 * const program = Effect.gen(function*() {
1340 * // Create a user profile map
1341 * const profiles = yield* TxHashMap.make(
1342 * ["alice", { name: "Alice", age: 30, active: true }],
1343 * ["bob", { name: "Bob", age: 25, active: false }],
1344 * ["charlie", { name: "Charlie", age: 35, active: true }]
1345 * )
1346 *
1347 * // Transform to extract just names with greeting
1348 * const greetings = yield* TxHashMap.map(
1349 * profiles,
1350 * (profile, userId) => `Hello, ${profile.name}! (User: ${userId})`
1351 * )
1352 *
1353 * // Check the transformed values
1354 * yield* TxHashMap.get(greetings, "alice") // => Option.some("Hello, Alice! (User: alice)")
1355 *
1356 * // Data-last usage with pipe
1357 * const ages = yield* profiles.pipe(
1358 * TxHashMap.map((profile) => profile.age)
1359 * )
1360 *
1361 * yield* TxHashMap.get(ages, "alice") // => Option.some(30)
1362 *
1363 * // Original map is unchanged
1364 * return yield* TxHashMap.get(profiles, "alice")
1365 * })
1366 *
1367 * await Effect.runPromise(program) // => Option.some({ name: "Alice", age: 30, active: true })
1368 * ```
1369 *
1370 * @category combinators
1371 * @since 4.0.0
1372 */
1373export const map: {
1374 <A, V, K>(
1375 f: (value: V, key: K) => A
1376 ): (self: TxHashMap<K, V>) => Effect.Effect<TxHashMap<K, A>>
1377 <K, V, A>(
1378 self: TxHashMap<K, V>,
1379 f: (value: V, key: K) => A
1380 ): Effect.Effect<TxHashMap<K, A>>
1381} = dual(
1382 2,
1383 <K, V, A>(
1384 self: TxHashMap<K, V>,
1385 f: (value: V, key: K) => A
1386 ): Effect.Effect<TxHashMap<K, A>> =>
1387 Effect.gen(function*() {
1388 const currentMap = yield* TxRef.get(self.ref)
1389 const mappedMap = HashMap.map(currentMap, f)
1390 return yield* fromHashMap(mappedMap)
1391 }).pipe(Effect.tx)
1392)
1393
1394/**
1395 * Filters the TxHashMap to keep only entries that satisfy the provided predicate.
1396 *
1397 * **Details**
1398 *
1399 * This function returns a new TxHashMap reference containing only the entries
1400 * that match the condition. The original TxHashMap is not modified.
1401 *
1402 * **Example** (Filtering entries)
1403 *
1404 * ```ts import.meta.vitest
1405 * import { Effect, TxHashMap } from "effect"
1406 *
1407 * const program = Effect.gen(function*() {
1408 * // Create a product inventory
1409 * const inventory = yield* TxHashMap.make(
1410 * ["laptop", { price: 999, stock: 5, category: "electronics" }],
1411 * ["mouse", { price: 29, stock: 50, category: "electronics" }],
1412 * ["book", { price: 15, stock: 100, category: "books" }],
1413 * ["phone", { price: 699, stock: 0, category: "electronics" }]
1414 * )
1415 *
1416 * // Filter to get only electronics in stock
1417 * const electronicsInStock = yield* TxHashMap.filter(
1418 * inventory,
1419 * (product) => product.category === "electronics" && product.stock > 0
1420 * )
1421 *
1422 * yield* TxHashMap.size(electronicsInStock) // => 2
1423 *
1424 * // Data-last usage with pipe
1425 * const expensiveItems = yield* inventory.pipe(
1426 * TxHashMap.filter((product) => product.price > 500)
1427 * )
1428 *
1429 * yield* TxHashMap.size(expensiveItems) // => 2
1430 *
1431 * // Type guard usage
1432 * return yield* TxHashMap.filter(
1433 * inventory,
1434 * (product): product is typeof product & { price: number } =>
1435 * product.price > 50
1436 * )
1437 * })
1438 *
1439 * await Effect.runPromise(program)
1440 * ```
1441 *
1442 * @category combinators
1443 * @since 4.0.0
1444 */
1445export const filter: {
1446 <K, V, B extends V>(
1447 predicate: (value: V, key: K) => value is B
1448 ): (self: TxHashMap<K, V>) => Effect.Effect<TxHashMap<K, B>>
1449 <K, V>(
1450 predicate: (value: V, key: K) => boolean
1451 ): (self: TxHashMap<K, V>) => Effect.Effect<TxHashMap<K, V>>
1452 <K, V, B extends V>(
1453 self: TxHashMap<K, V>,
1454 predicate: (value: V, key: K) => value is B
1455 ): Effect.Effect<TxHashMap<K, B>>
1456 <K, V>(
1457 self: TxHashMap<K, V>,
1458 predicate: (value: V, key: K) => boolean
1459 ): Effect.Effect<TxHashMap<K, V>>
1460} = dual(
1461 2,
1462 <K, V>(
1463 self: TxHashMap<K, V>,
1464 predicate: (value: V, key: K) => boolean
1465 ): Effect.Effect<TxHashMap<K, V>> =>
1466 Effect.gen(function*() {
1467 const currentMap = yield* TxRef.get(self.ref)
1468 const filteredMap = HashMap.filter(currentMap, predicate)
1469 return yield* fromHashMap(filteredMap)
1470 }).pipe(Effect.tx)
1471)
1472
1473/**
1474 * Reduces the TxHashMap entries to a single value by applying a reducer function.
1475 * Iterates over all key-value pairs and accumulates them into a final result.
1476 *
1477 * **Example** (Reducing entries)
1478 *
1479 * ```ts import.meta.vitest
1480 * import { Effect, TxHashMap } from "effect"
1481 *
1482 * const program = Effect.gen(function*() {
1483 * // Create a sales data map
1484 * const sales = yield* TxHashMap.make(
1485 * ["Q1", 15000],
1486 * ["Q2", 18000],
1487 * ["Q3", 22000],
1488 * ["Q4", 25000]
1489 * )
1490 *
1491 * // Calculate total sales
1492 * const totalSales = yield* TxHashMap.reduce(
1493 * sales,
1494 * 0,
1495 * (total, amount) => total + amount
1496 * )
1497 * totalSales // => 80000
1498 *
1499 * // Data-last usage with pipe
1500 * const quarterlyReport = yield* sales.pipe(
1501 * TxHashMap.reduce(
1502 * { quarters: 0, total: 0, max: 0 },
1503 * (report, amount, quarter) => ({
1504 * quarters: report.quarters + 1,
1505 * total: report.total + amount,
1506 * max: Math.max(report.max, amount)
1507 * })
1508 * )
1509 * )
1510 * return quarterlyReport
1511 * })
1512 *
1513 * await Effect.runPromise(program) // => { quarters: 4, total: 80000, max: 25000 }
1514 * ```
1515 *
1516 * @category combinators
1517 * @since 2.0.0
1518 */
1519export const reduce: {
1520 <A, V, K>(
1521 zero: A,
1522 f: (accumulator: A, value: V, key: K) => A
1523 ): (self: TxHashMap<K, V>) => Effect.Effect<A>
1524 <K, V, A>(
1525 self: TxHashMap<K, V>,
1526 zero: A,
1527 f: (accumulator: A, value: V, key: K) => A
1528 ): Effect.Effect<A>
1529} = dual(
1530 3,
1531 <K, V, A>(
1532 self: TxHashMap<K, V>,
1533 zero: A,
1534 f: (accumulator: A, value: V, key: K) => A
1535 ): Effect.Effect<A> => TxRef.get(self.ref).pipe(Effect.map((map) => HashMap.reduce(map, zero, f)))
1536)
1537
1538/**
1539 * Combines filtering and mapping in a single operation. Applies a filter to each
1540 * entry, keeping only successful results and transforming them.
1541 *
1542 * **Details**
1543 *
1544 * This function returns a new TxHashMap reference containing only the transformed
1545 * entries that succeeded. The original TxHashMap is not modified.
1546 *
1547 * **Example** (Filtering and mapping entries)
1548 *
1549 * ```ts import.meta.vitest
1550 * import { Effect, Option, Result, TxHashMap } from "effect"
1551 *
1552 * const program = Effect.gen(function*() {
1553 * // Create a mixed data map
1554 * const userData = yield* TxHashMap.make(
1555 * ["alice", { age: "30", role: "admin", active: true }],
1556 * ["bob", { age: "invalid", role: "user", active: true }],
1557 * ["charlie", { age: "25", role: "admin", active: false }],
1558 * ["diana", { age: "28", role: "user", active: true }]
1559 * )
1560 *
1561 * // Extract valid ages for active admin users only
1562 * const activeAdminAges = yield* TxHashMap.filterMap(
1563 * userData,
1564 * (user, username) => {
1565 * if (!user.active || user.role !== "admin") return Result.failVoid
1566 * const age = parseInt(user.age)
1567 * if (isNaN(age)) return Result.failVoid
1568 * return Result.succeed({
1569 * username,
1570 * age,
1571 * seniority: age > 27 ? "senior" : "junior"
1572 * })
1573 * }
1574 * )
1575 *
1576 * const aliceData = yield* TxHashMap.get(activeAdminAges, "alice")
1577 * aliceData // => Option.some({ username: "alice", age: 30, seniority: "senior" })
1578 * yield* TxHashMap.get(activeAdminAges, "charlie") // => Option.none()
1579 *
1580 * // Data-last usage with pipe
1581 * const validAges = yield* userData.pipe(
1582 * TxHashMap.filterMap((user) => {
1583 * const age = parseInt(user.age)
1584 * return isNaN(age) ? Result.failVoid : Result.succeed(age)
1585 * })
1586 * )
1587 *
1588 * return yield* TxHashMap.size(validAges)
1589 * })
1590 *
1591 * await Effect.runPromise(program) // => 3
1592 * ```
1593 *
1594 * @category combinators
1595 * @since 4.0.0
1596 */
1597export const filterMap: {
1598 <V, K, A, X>(
1599 f: (input: V, key: K) => Result<A, X>
1600 ): (self: TxHashMap<K, V>) => Effect.Effect<TxHashMap<K, A>>
1601 <K, V, A, X>(
1602 self: TxHashMap<K, V>,
1603 f: (input: V, key: K) => Result<A, X>
1604 ): Effect.Effect<TxHashMap<K, A>>
1605} = dual(
1606 2,
1607 <K, V, A, X>(
1608 self: TxHashMap<K, V>,
1609 f: (input: V, key: K) => Result<A, X>
1610 ): Effect.Effect<TxHashMap<K, A>> =>
1611 Effect.gen(function*() {
1612 const currentMap = yield* TxRef.get(self.ref)
1613 const filteredMap = HashMap.filterMap(currentMap, f)
1614 return yield* fromHashMap(filteredMap)
1615 }).pipe(Effect.tx)
1616)
1617
1618/**
1619 * Checks whether any entry in the TxHashMap matches the given predicate.
1620 *
1621 * **Example** (Checking entries with a predicate)
1622 *
1623 * ```ts import.meta.vitest
1624 * import { Effect, TxHashMap } from "effect"
1625 *
1626 * const program = Effect.gen(function*() {
1627 * // Create a user status map
1628 * const currentTime = 1_700_000_000_000
1629 * const userStatuses = yield* TxHashMap.make(
1630 * ["alice", { status: "online", lastSeen: currentTime }],
1631 * ["bob", { status: "offline", lastSeen: currentTime - 3_600_000 }],
1632 * ["charlie", { status: "online", lastSeen: currentTime }]
1633 * )
1634 *
1635 * // Check if any users are online
1636 * yield* TxHashMap.hasBy(
1637 * userStatuses,
1638 * (user) => user.status === "online"
1639 * ) // => true
1640 *
1641 * // Check if any users have specific username pattern
1642 * yield* TxHashMap.hasBy(
1643 * userStatuses,
1644 * (user, username) => username.startsWith("admin")
1645 * ) // => false
1646 *
1647 * // Data-last usage with pipe
1648 * return yield* userStatuses.pipe(
1649 * TxHashMap.hasBy((user) => currentTime - user.lastSeen < 1_800_000) // 30 minutes
1650 * )
1651 * })
1652 *
1653 * await Effect.runPromise(program) // => true
1654 * ```
1655 *
1656 * @category predicates
1657 * @since 4.0.0
1658 */
1659export const hasBy: {
1660 <K, V>(
1661 predicate: (value: V, key: K) => boolean
1662 ): (self: TxHashMap<K, V>) => Effect.Effect<boolean>
1663 <K, V>(
1664 self: TxHashMap<K, V>,
1665 predicate: (value: V, key: K) => boolean
1666 ): Effect.Effect<boolean>
1667} = dual(
1668 2,
1669 <K, V>(
1670 self: TxHashMap<K, V>,
1671 predicate: (value: V, key: K) => boolean
1672 ): Effect.Effect<boolean> => TxRef.get(self.ref).pipe(Effect.map((map) => HashMap.hasBy(map, predicate)))
1673)
1674
1675/**
1676 * Finds the first entry in the TxHashMap that matches the given predicate.
1677 * Returns the key-value pair as a tuple wrapped in an Option.
1678 *
1679 * **Example** (Finding the first matching entry)
1680 *
1681 * ```ts import.meta.vitest
1682 * import { Effect, Option, TxHashMap } from "effect"
1683 *
1684 * const program = Effect.gen(function*() {
1685 * // Create a task priority map
1686 * const tasks = yield* TxHashMap.make(
1687 * ["task1", { priority: 1, assignee: "alice", completed: false }],
1688 * ["task2", { priority: 3, assignee: "bob", completed: true }],
1689 * ["task3", { priority: 2, assignee: "alice", completed: false }]
1690 * )
1691 *
1692 * // Find first high-priority incomplete task
1693 * const highPriorityTask = yield* TxHashMap.findFirst(
1694 * tasks,
1695 * (task) => task.priority >= 2 && !task.completed
1696 * )
1697 *
1698 * highPriorityTask // => Option.some(["task3", { priority: 2, assignee: "alice", completed: false }])
1699 *
1700 * // Find first task assigned to specific user
1701 * return yield* tasks.pipe(
1702 * TxHashMap.findFirst((task) => task.assignee === "alice")
1703 * )
1704 * })
1705 *
1706 * await Effect.runPromise(program) // => Option.some(["task1", { priority: 1, assignee: "alice", completed: false }])
1707 * ```
1708 *
1709 * @category combinators
1710 * @since 4.0.0
1711 */
1712export const findFirst: {
1713 <K, V>(
1714 predicate: (value: V, key: K) => boolean
1715 ): (self: TxHashMap<K, V>) => Effect.Effect<Option.Option<[K, V]>>
1716 <K, V>(
1717 self: TxHashMap<K, V>,
1718 predicate: (value: V, key: K) => boolean
1719 ): Effect.Effect<Option.Option<[K, V]>>
1720} = dual(
1721 2,
1722 <K, V>(
1723 self: TxHashMap<K, V>,
1724 predicate: (value: V, key: K) => boolean
1725 ): Effect.Effect<Option.Option<[K, V]>> =>
1726 TxRef.get(self.ref).pipe(Effect.map((map) => HashMap.findFirst(map, predicate)))
1727)
1728
1729/**
1730 * Checks whether at least one entry in the TxHashMap satisfies the given predicate.
1731 *
1732 * **Example** (Checking whether some entries match)
1733 *
1734 * ```ts import.meta.vitest
1735 * import { Effect, TxHashMap } from "effect"
1736 *
1737 * const program = Effect.gen(function*() {
1738 * // Create a product inventory
1739 * const inventory = yield* TxHashMap.make(
1740 * ["laptop", { price: 999, stock: 5 }],
1741 * ["mouse", { price: 29, stock: 50 }],
1742 * ["keyboard", { price: 79, stock: 0 }]
1743 * )
1744 *
1745 * // Check if any products are expensive
1746 * yield* TxHashMap.some(
1747 * inventory,
1748 * (product) => product.price > 500
1749 * ) // => true
1750 *
1751 * // Check if any products are out of stock
1752 * yield* TxHashMap.some(
1753 * inventory,
1754 * (product) => product.stock === 0
1755 * ) // => true
1756 *
1757 * // Data-last usage with pipe
1758 * return yield* inventory.pipe(
1759 * TxHashMap.some((product) => product.price < 50)
1760 * )
1761 * })
1762 *
1763 * await Effect.runPromise(program) // => true
1764 * ```
1765 *
1766 * @category predicates
1767 * @since 4.0.0
1768 */
1769export const some: {
1770 <K, V>(
1771 predicate: (value: V, key: K) => boolean
1772 ): (self: TxHashMap<K, V>) => Effect.Effect<boolean>
1773 <K, V>(
1774 self: TxHashMap<K, V>,
1775 predicate: (value: V, key: K) => boolean
1776 ): Effect.Effect<boolean>
1777} = dual(
1778 2,
1779 <K, V>(
1780 self: TxHashMap<K, V>,
1781 predicate: (value: V, key: K) => boolean
1782 ): Effect.Effect<boolean> => TxRef.get(self.ref).pipe(Effect.map((map) => HashMap.some(map, predicate)))
1783)
1784
1785/**
1786 * Checks whether all entries in the TxHashMap satisfy the given predicate.
1787 *
1788 * **Example** (Checking whether every entry matches)
1789 *
1790 * ```ts import.meta.vitest
1791 * import { Effect, TxHashMap } from "effect"
1792 *
1793 * const program = Effect.gen(function*() {
1794 * // Create a user permissions map
1795 * const permissions = yield* TxHashMap.make(
1796 * ["alice", { canRead: true, canWrite: true, canDelete: false }],
1797 * ["bob", { canRead: true, canWrite: false, canDelete: false }],
1798 * ["charlie", { canRead: true, canWrite: true, canDelete: true }]
1799 * )
1800 *
1801 * // Check if all users can read
1802 * yield* TxHashMap.every(
1803 * permissions,
1804 * (perms) => perms.canRead
1805 * ) // => true
1806 *
1807 * // Check if all users can write
1808 * yield* TxHashMap.every(
1809 * permissions,
1810 * (perms) => perms.canWrite
1811 * ) // => false
1812 *
1813 * // Data-last usage with pipe
1814 * return yield* permissions.pipe(
1815 * TxHashMap.every((perms, username) => perms.canRead && username.length > 2)
1816 * )
1817 * })
1818 *
1819 * await Effect.runPromise(program) // => true
1820 * ```
1821 *
1822 * @category predicates
1823 * @since 4.0.0
1824 */
1825export const every: {
1826 <K, V>(
1827 predicate: (value: V, key: K) => boolean
1828 ): (self: TxHashMap<K, V>) => Effect.Effect<boolean>
1829 <K, V>(
1830 self: TxHashMap<K, V>,
1831 predicate: (value: V, key: K) => boolean
1832 ): Effect.Effect<boolean>
1833} = dual(
1834 2,
1835 <K, V>(
1836 self: TxHashMap<K, V>,
1837 predicate: (value: V, key: K) => boolean
1838 ): Effect.Effect<boolean> => TxRef.get(self.ref).pipe(Effect.map((map) => HashMap.every(map, predicate)))
1839)
1840
1841/**
1842 * Executes a side-effect function for each entry in the TxHashMap.
1843 * The function receives the value and key as parameters and can perform effects.
1844 *
1845 * **Example** (Running effects for each entry)
1846 *
1847 * ```ts import.meta.vitest
1848 * import { Effect, TxHashMap } from "effect"
1849 *
1850 * const program = Effect.gen(function*() {
1851 * // Create a log processing map
1852 * const logs = yield* TxHashMap.make(
1853 * ["error.log", { size: 1024, level: "error" }],
1854 * ["access.log", { size: 2048, level: "info" }],
1855 * ["debug.log", { size: 512, level: "debug" }]
1856 * )
1857 *
1858 * const messages: Array<string> = []
1859 * yield* TxHashMap.forEach(logs, (logInfo, filename) =>
1860 * Effect.sync(() => {
1861 * messages.push(`${filename}: ${logInfo.size} bytes (${logInfo.level})`)
1862 * }))
1863 *
1864 * return messages.sort()
1865 * })
1866 *
1867 * const result = await Effect.runPromise(program)
1868 * result // => ["access.log: 2048 bytes (info)", "debug.log: 512 bytes (debug)", "error.log: 1024 bytes (error)"]
1869 * ```
1870 *
1871 * @category combinators
1872 * @since 2.0.0
1873 */
1874export const forEach: {
1875 <V, K, R, E>(
1876 f: (value: V, key: K) => Effect.Effect<void, E, R>
1877 ): (self: TxHashMap<K, V>) => Effect.Effect<void, E, R>
1878 <K, V, R, E>(
1879 self: TxHashMap<K, V>,
1880 f: (value: V, key: K) => Effect.Effect<void, E, R>
1881 ): Effect.Effect<void, E, R>
1882} = dual(
1883 2,
1884 <K, V, R, E>(
1885 self: TxHashMap<K, V>,
1886 f: (value: V, key: K) => Effect.Effect<void, E, R>
1887 ): Effect.Effect<void, E, R> =>
1888 Effect.gen(function*() {
1889 const currentMap = yield* TxRef.get(self.ref)
1890 const entries = HashMap.toEntries(currentMap)
1891 yield* Effect.forEach(entries, ([key, value]) => f(value, key))
1892 })
1893)
1894
1895/**
1896 * Maps each entry effectfully to a `TxHashMap` and flattens the produced maps.
1897 *
1898 * **Details**
1899 *
1900 * This function returns a new TxHashMap reference with the flattened results.
1901 * The original TxHashMap is not modified.
1902 *
1903 * **Example** (Flat mapping entries)
1904 *
1905 * ```ts import.meta.vitest
1906 * import { Effect, Option, TxHashMap } from "effect"
1907 *
1908 * const program = Effect.gen(function*() {
1909 * // Create a department-employee map
1910 * const departments = yield* TxHashMap.make(
1911 * ["engineering", ["alice", "bob"]],
1912 * ["marketing", ["charlie", "diana"]]
1913 * )
1914 *
1915 * // Expand each department into individual employee entries with metadata
1916 * const employeeDetails = yield* TxHashMap.flatMap(
1917 * departments,
1918 * (employees, department) =>
1919 * Effect.gen(function*() {
1920 * const employeeMap = yield* TxHashMap.empty<
1921 * string,
1922 * { department: string; role: string }
1923 * >()
1924 * for (let i = 0; i < employees.length; i++) {
1925 * const employee = employees[i]
1926 * const role = i === 0 ? "lead" : "member"
1927 * yield* TxHashMap.set(employeeMap, employee, { department, role })
1928 * }
1929 * return employeeMap
1930 * })
1931 * )
1932 *
1933 * // Check the flattened result
1934 * yield* TxHashMap.get(employeeDetails, "alice") // => Option.some({ department: "engineering", role: "lead" })
1935 * yield* TxHashMap.get(employeeDetails, "charlie") // => Option.some({ department: "marketing", role: "lead" })
1936 * return yield* TxHashMap.size(employeeDetails)
1937 * })
1938 *
1939 * await Effect.runPromise(program) // => 4
1940 * ```
1941 *
1942 * @category combinators
1943 * @since 4.0.0
1944 */
1945export const flatMap: {
1946 <A, V, K>(
1947 f: (value: V, key: K) => Effect.Effect<TxHashMap<K, A>>
1948 ): (self: TxHashMap<K, V>) => Effect.Effect<TxHashMap<K, A>>
1949 <K, V, A>(
1950 self: TxHashMap<K, V>,
1951 f: (value: V, key: K) => Effect.Effect<TxHashMap<K, A>>
1952 ): Effect.Effect<TxHashMap<K, A>>
1953} = dual(
1954 2,
1955 <K, V, A>(
1956 self: TxHashMap<K, V>,
1957 f: (value: V, key: K) => Effect.Effect<TxHashMap<K, A>>
1958 ): Effect.Effect<TxHashMap<K, A>> =>
1959 Effect.gen(function*() {
1960 const currentMap = yield* TxRef.get(self.ref)
1961 const result = yield* empty<K, A>()
1962
1963 const mapEntries = HashMap.toEntries(currentMap)
1964 for (const [key, value] of mapEntries) {
1965 const newMap = yield* f(value, key)
1966 const newEntries = yield* entries(newMap)
1967 yield* setMany(result, newEntries)
1968 }
1969
1970 return result
1971 }).pipe(Effect.tx)
1972)
1973
1974/**
1975 * Removes all None values from a TxHashMap containing Option values.
1976 *
1977 * **Details**
1978 *
1979 * This function returns a new TxHashMap reference with only the Some values
1980 * unwrapped. The original TxHashMap is not modified.
1981 *
1982 * **Example** (Compacting optional values)
1983 *
1984 * ```ts import.meta.vitest
1985 * import { Effect, Option, TxHashMap } from "effect"
1986 *
1987 * const program = Effect.gen(function*() {
1988 * // Create a map with optional user data
1989 * const userData = yield* TxHashMap.make<
1990 * string,
1991 * Option.Option<{ age: number; email?: string }>
1992 * >(
1993 * ["alice", Option.some({ age: 30, email: "alice@example.com" })],
1994 * ["bob", Option.none()], // incomplete data
1995 * ["charlie", Option.some({ age: 25 })],
1996 * ["diana", Option.none()], // missing data
1997 * ["eve", Option.some({ age: 28, email: "eve@example.com" })]
1998 * )
1999 *
2000 * // Remove all None values and unwrap Some values
2001 * const validUsers = yield* TxHashMap.compact(userData)
2002 *
2003 * yield* TxHashMap.size(validUsers) // => 3
2004 *
2005 * yield* TxHashMap.get(validUsers, "alice") // => Option.some({ age: 30, email: "alice@example.com" })
2006 * yield* TxHashMap.get(validUsers, "bob") // => Option.none()
2007 *
2008 * // Useful for cleaning up optional data processing results
2009 * const userAges = yield* TxHashMap.map(validUsers, (user) => user.age)
2010 * return (yield* TxHashMap.entries(userAges)).toSorted(([left], [right]) => left.localeCompare(right))
2011 * })
2012 *
2013 * await Effect.runPromise(program) // => [["alice", 30], ["charlie", 25], ["eve", 28]]
2014 * ```
2015 *
2016 * @category combinators
2017 * @since 4.0.0
2018 */
2019export const compact = <K, A>(
2020 self: TxHashMap<K, Option.Option<A>>
2021): Effect.Effect<TxHashMap<K, A>> =>
2022 Effect.gen(function*() {
2023 const currentMap = yield* TxRef.get(self.ref)
2024 const compactedMap = HashMap.compact(currentMap)
2025 return yield* fromHashMap(compactedMap)
2026 }).pipe(Effect.tx)
2027
2028/**
2029 * Returns an array of all key-value pairs in the TxHashMap.
2030 * This is an alias for the `entries` function, providing API consistency with HashMap.
2031 *
2032 * **Example** (Converting to entries)
2033 *
2034 * ```ts import.meta.vitest
2035 * import { Effect, TxHashMap } from "effect"
2036 *
2037 * const program = Effect.gen(function*() {
2038 * const settings = yield* TxHashMap.make(
2039 * ["theme", "dark"],
2040 * ["language", "en-US"],
2041 * ["timezone", "UTC"]
2042 * )
2043 *
2044 * // Get all entries as an array
2045 * const sortedEntries = (yield* TxHashMap.toEntries(settings))
2046 * .toSorted(([left], [right]) => left.localeCompare(right))
2047 * sortedEntries // => [["language", "en-US"], ["theme", "dark"], ["timezone", "UTC"]]
2048 *
2049 * // Convert to an object
2050 * return Object.fromEntries(sortedEntries)
2051 * })
2052 *
2053 * await Effect.runPromise(program) // => { language: "en-US", theme: "dark", timezone: "UTC" }
2054 * ```
2055 *
2056 * @category getters
2057 * @since 4.0.0
2058 */
2059export const toEntries = <K, V>(
2060 self: TxHashMap<K, V>
2061): Effect.Effect<Array<readonly [K, V]>> => entries(self)
2062
2063/**
2064 * Returns an array of all values in the TxHashMap.
2065 * This is an alias for the `values` function, providing API consistency with HashMap.
2066 *
2067 * **Example** (Converting to values)
2068 *
2069 * ```ts import.meta.vitest
2070 * import { Effect, TxHashMap } from "effect"
2071 *
2072 * const program = Effect.gen(function*() {
2073 * const inventory = yield* TxHashMap.make(
2074 * ["laptop", { price: 999, stock: 5 }],
2075 * ["mouse", { price: 29, stock: 50 }],
2076 * ["keyboard", { price: 79, stock: 20 }]
2077 * )
2078 *
2079 * // Get all product information
2080 * const products = yield* TxHashMap.toValues(inventory)
2081 * products.length // => 3
2082 *
2083 * // Calculate total inventory value
2084 * const totalValue = products.reduce(
2085 * (sum, product) => sum + (product.price * product.stock),
2086 * 0
2087 * )
2088 * totalValue // => 8025
2089 *
2090 * // Find products with low stock
2091 * return products.filter((product) => product.stock < 10).length
2092 * })
2093 *
2094 * await Effect.runPromise(program) // => 1
2095 * ```
2096 *
2097 * @category getters
2098 * @since 4.0.0
2099 */
2100export const toValues = <K, V>(self: TxHashMap<K, V>): Effect.Effect<Array<V>> => values(self)
2101
2102/**
2103 * Helper function to create a TxHashMap from an existing HashMap
2104 */
2105const fromHashMap = <K, V>(hashMap: HashMap.HashMap<K, V>): Effect.Effect<TxHashMap<K, V>> =>
2106 Effect.gen(function*() {
2107 const ref = yield* TxRef.make(hashMap)
2108 return Object.assign(Object.create(TxHashMapProto), { ref })
2109 })