1/**
2 * Stores key/value entries in an immutable hash map.
3 *
4 * A `HashMap<Key, Value>` hashes keys and resolves matches with Effect's
5 * structural equality rules. Lookup, insertion, removal, and transformation
6 * operations return new maps, while temporary mutation helpers support efficient
7 * batch updates. This module also includes constructors, iteration, conversion,
8 * mapping, filtering, and reducing helpers.
9 *
10 * @since 2.0.0
11 */
12
13import type { Equal } from "./Equal.ts"
14import type { Inspectable } from "./Inspectable.ts"
15import * as internal from "./internal/hashMap.ts"
16import type { Option } from "./Option.ts"
17import type { Pipeable } from "./Pipeable.ts"
18import type { Result } from "./Result.ts"
19import type { NoInfer } from "./Types.ts"
20
21const TypeId = internal.HashMapTypeId
22
23/**
24 * A HashMap is an immutable key-value data structure that provides efficient lookup,
25 * insertion, and deletion operations. It uses a Hash Array Mapped Trie (HAMT) internally
26 * for structural sharing and optimal performance.
27 *
28 * **Example** (Using basic HashMap operations)
29 *
30 * ```ts import.meta.vitest
31 * import { HashMap, Option } from "effect"
32 *
33 * // Create a HashMap
34 * const map = HashMap.make(["a", 1], ["b", 2], ["c", 3])
35 *
36 * // Access values
37 * HashMap.get(map, "a") // => Option.some(1)
38 * HashMap.get(map, "d") // => Option.none()
39 *
40 * // Check if key exists
41 * HashMap.has(map, "b") // => true
42 *
43 * // Add/update values (returns new HashMap)
44 * HashMap.set(map, "d", 4) // => HashMap.make(["a", 1], ["b", 2], ["c", 3], ["d", 4])
45 * ```
46 *
47 * @category models
48 * @since 2.0.0
49 */
50export interface HashMap<out Key, out Value> extends Iterable<[Key, Value]>, Equal, Pipeable, Inspectable {
51 readonly [TypeId]: typeof TypeId
52}
53
54/**
55 * The HashMap namespace contains type-level utilities and helper types
56 * for working with HashMap instances.
57 *
58 * **Example** (Extracting HashMap types)
59 *
60 * ```ts import.meta.vitest
61 * import { HashMap } from "effect"
62 *
63 * // Create a concrete HashMap for type extraction
64 * const inventory = HashMap.make(
65 * ["laptop", { quantity: 5, price: 999 }],
66 * ["mouse", { quantity: 20, price: 29 }]
67 * )
68 *
69 * // Extract types for reuse
70 * type ProductId = HashMap.HashMap.Key<typeof inventory> // string
71 * type Product = HashMap.HashMap.Value<typeof inventory> // { quantity: number, price: number }
72 * type InventoryEntry = HashMap.HashMap.Entry<typeof inventory> // [string, Product]
73 *
74 * // Use extracted types in functions
75 * const updateInventory = (id: ProductId, product: Product) =>
76 * HashMap.set(inventory, id, product)
77 *
78 * const processEntry = ([id, product]: InventoryEntry) =>
79 * `${id}: ${product.quantity} @ $${product.price}`
80 *
81 * // Example of extracted types in action
82 * const newProduct: Product = { quantity: 10, price: 199 }
83 * const updatedInventory = updateInventory("tablet", newProduct)
84 * processEntry(["tablet", newProduct]) // => "tablet: 10 @ $199"
85 * updatedInventory // => HashMap.make(["laptop", { quantity: 5, price: 999 }], ["mouse", { quantity: 20, price: 29 }], ["tablet", newProduct])
86 * ```
87 *
88 * @since 2.0.0
89 */
90export declare namespace HashMap {
91 /**
92 * A function that updates a value based on its current state.
93 * Takes an Option representing the current value and returns an Option
94 * representing the new value.
95 *
96 * **Example** (Updating values from Options)
97 *
98 * ```ts import.meta.vitest
99 * import { HashMap, Option } from "effect"
100 *
101 * const map = HashMap.make(["a", 1], ["b", 2])
102 *
103 * // Increment existing value or set to 1 if not present
104 * const updateFn = (option: Option.Option<number>) =>
105 * Option.isSome(option) ? Option.some(option.value + 1) : Option.some(1)
106 *
107 * const updated = HashMap.modifyAt(map, "a", updateFn)
108 * HashMap.get(updated, "a") // => Option.some(2)
109 * ```
110 *
111 * @category models
112 * @since 2.0.0
113 */
114 export type UpdateFn<V> = (option: Option<V>) => Option<V>
115
116 /**
117 * This type-level utility extracts the key type `K` from a `HashMap<K, V>` type.
118 *
119 * **Example** (Extracting key types)
120 *
121 * ```ts import.meta.vitest
122 * import { HashMap, Option } from "effect"
123 *
124 * // Create a HashMap to extract key type from
125 * const userMap = HashMap.make(
126 * ["alice", { name: "Alice", age: 30 }],
127 * ["bob", { name: "Bob", age: 25 }]
128 * )
129 *
130 * // Extract the key type (string)
131 * type UserKey = HashMap.HashMap.Key<typeof userMap>
132 *
133 * // Use the extracted type in functions
134 * const getUserById = (id: UserKey) => HashMap.get(userMap, id)
135 * getUserById("alice") // => Option.some({ name: "Alice", age: 30 })
136 * ```
137 *
138 * @category utility types
139 * @since 2.0.0
140 */
141 export type Key<T extends HashMap<any, any>> = [T] extends [HashMap<infer _K, infer _V>] ? _K : never
142
143 /**
144 * This type-level utility extracts the value type `V` from a `HashMap<K, V>` type.
145 *
146 * **Example** (Extracting value types)
147 *
148 * ```ts import.meta.vitest
149 * import { HashMap, Option } from "effect"
150 *
151 * // Create a HashMap with user data
152 * const userMap = HashMap.make(
153 * ["alice", { name: "Alice", age: 30, active: true }],
154 * ["bob", { name: "Bob", age: 25, active: false }]
155 * )
156 *
157 * // Extract the value type (User object)
158 * type User = HashMap.HashMap.Value<typeof userMap>
159 *
160 * // Use the extracted type for type-safe operations
161 * const processUser = (user: User) => {
162 * return user.active ? `${user.name} (active)` : `${user.name} (inactive)`
163 * }
164 *
165 * // The lookup has type Option<User> thanks to type extraction
166 * HashMap.get(userMap, "alice") // => Option.some({ name: "Alice", age: 30, active: true })
167 * processUser({ name: "Alice", age: 30, active: true }) // => "Alice (active)"
168 * ```
169 *
170 * @category utility types
171 * @since 2.0.0
172 */
173 export type Value<T extends HashMap<any, any>> = [T] extends [HashMap<infer _K, infer _V>] ? _V : never
174
175 /**
176 * This type-level utility extracts the entry type `[K, V]` from a `HashMap<K, V>` type.
177 *
178 * **Example** (Extracting entry types)
179 *
180 * ```ts import.meta.vitest
181 * import { HashMap } from "effect"
182 *
183 * // Create a product catalog HashMap
184 * const catalog = HashMap.make(
185 * ["laptop", { price: 999, category: "electronics" }],
186 * ["book", { price: 29, category: "education" }]
187 * )
188 *
189 * // Extract the entry type [string, Product]
190 * type CatalogEntry = HashMap.HashMap.Entry<typeof catalog>
191 *
192 * // Use the extracted type for processing entries
193 * const processEntry = ([productId, product]: CatalogEntry) => {
194 * return `${productId}: $${product.price} (${product.category})`
195 * }
196 *
197 * // Convert to entries, process, and sort for deterministic output
198 * const descriptions = HashMap.toEntries(catalog).map(processEntry).sort()
199 * descriptions // => ["book: $29 (education)", "laptop: $999 (electronics)"]
200 * ```
201 *
202 * @category utility types
203 * @since 3.9.0
204 */
205 export type Entry<T extends HashMap<any, any>> = [Key<T>, Value<T>]
206}
207
208/**
209 * Checks whether a value is a HashMap.
210 *
211 * **Example** (Checking HashMap values)
212 *
213 * ```ts import.meta.vitest
214 * import { HashMap } from "effect"
215 *
216 * const map = HashMap.make(["a", 1], ["b", 2])
217 * const notMap = { a: 1 }
218 *
219 * HashMap.isHashMap(map) // => true
220 * HashMap.isHashMap(notMap) // => false
221 * HashMap.isHashMap(null) // => false
222 * ```
223 *
224 * @category guards
225 * @since 2.0.0
226 */
227export const isHashMap: {
228 <K, V>(u: Iterable<readonly [K, V]>): u is HashMap<K, V>
229 (u: unknown): u is HashMap<unknown, unknown>
230} = internal.isHashMap
231
232/**
233 * Creates a new empty `HashMap`.
234 *
235 * **Example** (Creating an empty HashMap)
236 *
237 * ```ts import.meta.vitest
238 * import { HashMap } from "effect"
239 *
240 * HashMap.empty<string, number>() // => HashMap.empty()
241 * ```
242 *
243 * @category constructors
244 * @since 2.0.0
245 */
246export const empty: <K = never, V = never>() => HashMap<K, V> = internal.empty
247
248/**
249 * Constructs a new `HashMap` from an array of key/value pairs.
250 *
251 * **Example** (Creating a HashMap from entries)
252 *
253 * ```ts import.meta.vitest
254 * import { HashMap } from "effect"
255 *
256 * HashMap.make(["a", 1], ["b", 2], ["c", 3]) // => HashMap.make(["a", 1], ["b", 2], ["c", 3])
257 * ```
258 *
259 * @category constructors
260 * @since 2.0.0
261 */
262export const make: <Entries extends ReadonlyArray<readonly [any, any]>>(
263 ...entries: Entries
264) => HashMap<
265 Entries[number] extends readonly [infer K, any] ? K : never,
266 Entries[number] extends readonly [any, infer V] ? V : never
267> = internal.make
268
269/**
270 * Creates a new `HashMap` from an iterable collection of key/value pairs.
271 *
272 * **Example** (Creating a HashMap from an iterable)
273 *
274 * ```ts import.meta.vitest
275 * import { HashMap } from "effect"
276 *
277 * const entries = [["a", 1], ["b", 2], ["c", 3]] as const
278 * HashMap.fromIterable(entries) // => HashMap.make(["a", 1], ["b", 2], ["c", 3])
279 * ```
280 *
281 * @category constructors
282 * @since 2.0.0
283 */
284export const fromIterable: <K, V>(entries: Iterable<readonly [K, V]>) => HashMap<K, V> = internal.fromIterable
285
286/**
287 * Checks whether the `HashMap` contains no entries.
288 *
289 * **Example** (Checking for empty HashMaps)
290 *
291 * ```ts import.meta.vitest
292 * import { HashMap } from "effect"
293 *
294 * const emptyMap = HashMap.empty<string, number>()
295 * const nonEmptyMap = HashMap.make(["a", 1])
296 *
297 * HashMap.isEmpty(emptyMap) // => true
298 * HashMap.isEmpty(nonEmptyMap) // => false
299 * ```
300 *
301 * @category predicates
302 * @since 2.0.0
303 */
304export const isEmpty: <K, V>(self: HashMap<K, V>) => boolean = internal.isEmpty
305
306/**
307 * Looks up the value for the specified key in the `HashMap` safely using the
308 * internal hashing function.
309 *
310 * **Example** (Looking up values)
311 *
312 * ```ts import.meta.vitest
313 * import { HashMap, Option } from "effect"
314 *
315 * const map = HashMap.make(["a", 1], ["b", 2])
316 *
317 * HashMap.get(map, "a") // => Option.some(1)
318 * HashMap.get(map, "c") // => Option.none()
319 *
320 * // Using pipe syntax
321 * HashMap.get("b")(map) // => Option.some(2)
322 * ```
323 *
324 * @category getters
325 * @since 2.0.0
326 */
327export const get: {
328 <K1 extends K, K>(key: K1): <V>(self: HashMap<K, V>) => Option<V>
329 <K1 extends K, K, V>(self: HashMap<K, V>, key: K1): Option<V>
330} = internal.get
331
332/**
333 * Looks up the value for the specified key in the `HashMap` safely using a custom hash.
334 *
335 * **Example** (Looking up values with a hash)
336 *
337 * ```ts import.meta.vitest
338 * import { Hash, HashMap, Option } from "effect"
339 *
340 * // Useful when implementing custom equality for complex keys
341 * const userMap = HashMap.make(
342 * ["user123", { name: "Alice", role: "admin" }],
343 * ["user456", { name: "Bob", role: "user" }]
344 * )
345 *
346 * // Use precomputed hash for performance in hot paths
347 * const userId = "user123"
348 * const precomputedHash = Hash.string(userId)
349 *
350 * // Lookup with custom hash (e.g., cached hash value)
351 * HashMap.getHash(userMap, userId, precomputedHash) // => Option.some({ name: "Alice", role: "admin" })
352 *
353 * // This avoids recomputing the hash when you already have it
354 * HashMap.getHash(userMap, "user999", Hash.string("user999")) // => Option.none()
355 * ```
356 *
357 * @category getters
358 * @since 2.0.0
359 */
360export const getHash: {
361 <K1 extends K, K>(key: K1, hash: number): <V>(self: HashMap<K, V>) => Option<V>
362 <K1 extends K, K, V>(self: HashMap<K, V>, key: K1, hash: number): Option<V>
363} = internal.getHash
364
365/**
366 * Looks up the value for the specified key in the `HashMap` unsafely using the
367 * internal hashing function.
368 *
369 * **When to use**
370 *
371 * Use when reading from a `HashMap` by a key known to exist, and throwing is an
372 * acceptable programming error for a missing key.
373 *
374 * **Gotchas**
375 *
376 * This function throws an error if the key is not found. Use `HashMap.get` for
377 * safe access that returns `Option`.
378 *
379 * **Example** (Unsafely looking up values)
380 *
381 * ```ts import.meta.vitest
382 * import { HashMap, Option } from "effect"
383 *
384 * const config = HashMap.make(
385 * ["api_url", "https://api.example.com"],
386 * ["timeout", "5000"],
387 * ["retries", "3"]
388 * )
389 *
390 * // Safe: use when you're certain the key exists
391 * HashMap.getUnsafe(config, "api_url") // => "https://api.example.com"
392 *
393 * // Preferred: use get() for uncertain keys
394 * HashMap.get(config, "db_url") // => Option.none()
395 *
396 * // This would throw: HashMap.getUnsafe(config, "db_url")
397 * // Error: "HashMap.getUnsafe: key not found"
398 * ```
399 *
400 * @category unsafe
401 * @since 4.0.0
402 */
403export const getUnsafe: {
404 <K1 extends K, K>(key: K1): <V>(self: HashMap<K, V>) => V
405 <K1 extends K, K, V>(self: HashMap<K, V>, key: K1): V
406} = internal.getUnsafe
407
408/**
409 * Checks whether the specified key has an entry in the `HashMap`.
410 *
411 * **Example** (Checking for keys)
412 *
413 * ```ts import.meta.vitest
414 * import { HashMap } from "effect"
415 *
416 * const map = HashMap.make(["a", 1], ["b", 2])
417 *
418 * HashMap.has(map, "a") // => true
419 * HashMap.has(map, "c") // => false
420 *
421 * // Using pipe syntax
422 * HashMap.has("b")(map) // => true
423 * ```
424 *
425 * @category predicates
426 * @since 2.0.0
427 */
428export const has: {
429 <K1 extends K, K>(key: K1): <K, V>(self: HashMap<K, V>) => boolean
430 <K1 extends K, K, V>(self: HashMap<K, V>, key: K1): boolean
431} = internal.has
432
433/**
434 * Checks whether the specified key has an entry in the `HashMap` using a custom
435 * hash.
436 *
437 * **Example** (Checking keys with a hash)
438 *
439 * ```ts import.meta.vitest
440 * import { Hash, HashMap } from "effect"
441 *
442 * // Create a map with case-sensitive keys
443 * const userMap = HashMap.make(
444 * ["Admin", { role: "administrator" }],
445 * ["User", { role: "standard" }]
446 * )
447 *
448 * // Check with exact hash
449 * const exactHash = Hash.string("Admin")
450 * HashMap.hasHash(userMap, "Admin", exactHash) // => true
451 *
452 * // A matching hash does not override key equality
453 * HashMap.hasHash(userMap, "admin", exactHash) // => false
454 *
455 * // A different hash also cannot find the existing key
456 * const lowercaseHash = Hash.string("admin")
457 * HashMap.hasHash(userMap, "Admin", lowercaseHash) // => false
458 * ```
459 *
460 * @category predicates
461 * @since 2.0.0
462 */
463export const hasHash: {
464 <K1 extends K, K>(key: K1, hash: number): <V>(self: HashMap<K, V>) => boolean
465 <K1 extends K, K, V>(self: HashMap<K, V>, key: K1, hash: number): boolean
466} = internal.hasHash
467
468/**
469 * Checks whether an element matching the given predicate exists in the given `HashMap`.
470 *
471 * **Example** (Checking entries by predicate)
472 *
473 * ```ts import.meta.vitest
474 * import { HashMap } from "effect"
475 *
476 * const hm = HashMap.make([1, "a"])
477 * HashMap.hasBy(hm, (value, key) => value === "a" && key === 1) // => true
478 * HashMap.hasBy(hm, (value) => value === "b") // => false
479 * ```
480 *
481 * @category predicates
482 * @since 3.16.0
483 */
484export const hasBy: {
485 <K, V>(predicate: (value: NoInfer<V>, key: NoInfer<K>) => boolean): (self: HashMap<K, V>) => boolean
486 <K, V>(self: HashMap<K, V>, predicate: (value: NoInfer<V>, key: NoInfer<K>) => boolean): boolean
487} = internal.hasBy
488
489/**
490 * Sets the specified key to the specified value using the internal hashing
491 * function.
492 *
493 * **Example** (Setting a value)
494 *
495 * ```ts import.meta.vitest
496 * import { HashMap } from "effect"
497 *
498 * const map1 = HashMap.make(["a", 1])
499 * HashMap.set(map1, "b", 2) // => HashMap.make(["a", 1], ["b", 2])
500 *
501 * // Original map is unchanged
502 * map1 // => HashMap.make(["a", 1])
503 * ```
504 *
505 * @category transforming
506 * @since 2.0.0
507 */
508export const set: {
509 <K, V>(key: K, value: V): (self: HashMap<K, V>) => HashMap<K, V>
510 <K, V>(self: HashMap<K, V>, key: K, value: V): HashMap<K, V>
511} = internal.set
512
513/**
514 * Returns an `IterableIterator` of the keys within the `HashMap`.
515 *
516 * **Example** (Iterating keys)
517 *
518 * ```ts import.meta.vitest
519 * import { HashMap } from "effect"
520 *
521 * const map = HashMap.make(["a", 1], ["b", 2], ["c", 3])
522 * Array.from(HashMap.keys(map)).sort() // => ["a", "b", "c"]
523 * ```
524 *
525 * @category getters
526 * @since 2.0.0
527 */
528export const keys: <K, V>(self: HashMap<K, V>) => IterableIterator<K> = internal.keys
529
530/**
531 * Returns an `IterableIterator` of the values within the `HashMap`.
532 *
533 * **Example** (Iterating values)
534 *
535 * ```ts import.meta.vitest
536 * import { HashMap } from "effect"
537 *
538 * const map = HashMap.make(["a", 1], ["b", 2], ["c", 3])
539 * Array.from(HashMap.values(map)).sort() // => [1, 2, 3]
540 * ```
541 *
542 * @category getters
543 * @since 2.0.0
544 */
545export const values: <K, V>(self: HashMap<K, V>) => IterableIterator<V> = internal.values
546
547/**
548 * Returns an `Array` of the values within the `HashMap`.
549 *
550 * **Example** (Converting values to an array)
551 *
552 * ```ts import.meta.vitest
553 * import { HashMap } from "effect"
554 *
555 * const employees = HashMap.make(
556 * ["alice", { department: "engineering", salary: 90000 }],
557 * ["bob", { department: "marketing", salary: 75000 }],
558 * ["charlie", { department: "engineering", salary: 95000 }]
559 * )
560 *
561 * // Extract all employee records
562 * const allEmployees = HashMap.toValues(employees)
563 * allEmployees.length // => 3
564 *
565 * // Calculate total salary
566 * allEmployees.reduce((sum, emp) => sum + emp.salary, 0) // => 260000
567 *
568 * // Filter by department
569 * allEmployees.filter((emp) => emp.department === "engineering").length // => 2
570 * ```
571 *
572 * @category getters
573 * @since 3.13.0
574 */
575export const toValues = <K, V>(self: HashMap<K, V>): Array<V> => Array.from(values(self))
576
577/**
578 * Returns an `IterableIterator` of the entries within the `HashMap`.
579 *
580 * **Example** (Iterating entries)
581 *
582 * ```ts import.meta.vitest
583 * import { HashMap } from "effect"
584 *
585 * // Create a configuration map
586 * const config = HashMap.make(
587 * ["database.host", "localhost"],
588 * ["database.port", "5432"],
589 * ["cache.enabled", "true"]
590 * )
591 *
592 * // Sort the derived array for deterministic output
593 * const settings = Array.from(HashMap.entries(config))
594 * .sort(([left], [right]) => left.localeCompare(right))
595 * .map(([key, value]) => `Setting ${key} = ${value}`)
596 *
597 * settings // => ["Setting cache.enabled = true", "Setting database.host = localhost", "Setting database.port = 5432"]
598 *
599 * // Convert to array when you need all entries at once
600 * Array.from(HashMap.entries(config)).length // => 3
601 * ```
602 *
603 * @category getters
604 * @since 2.0.0
605 */
606export const entries: <K, V>(self: HashMap<K, V>) => IterableIterator<[K, V]> = internal.entries
607
608/**
609 * Returns an `Array<[K, V]>` of the entries within the `HashMap`.
610 *
611 * **Example** (Converting entries to an array)
612 *
613 * ```ts import.meta.vitest
614 * import { HashMap } from "effect"
615 *
616 * const gameScores = HashMap.make(
617 * ["alice", 1250],
618 * ["bob", 980],
619 * ["charlie", 1100]
620 * )
621 *
622 * // Convert to entries for processing
623 * const scoreEntries = HashMap.toEntries(gameScores)
624 *
625 * // Sort by score (descending)
626 * const leaderboard = scoreEntries
627 * .sort(([, a], [, b]) => b - a)
628 * .map(([player, score], rank) => `${rank + 1}. ${player}: ${score}`)
629 *
630 * leaderboard // => ["1. alice: 1250", "2. charlie: 1100", "3. bob: 980"]
631 *
632 * // Convert back to HashMap if needed
633 * HashMap.fromIterable(scoreEntries) // => HashMap.make(["alice", 1250], ["charlie", 1100], ["bob", 980])
634 * ```
635 *
636 * @category getters
637 * @since 2.0.0
638 */
639export const toEntries = <K, V>(self: HashMap<K, V>): Array<[K, V]> => Array.from(entries(self))
640
641/**
642 * Returns the number of entries within the `HashMap`.
643 *
644 * **Example** (Getting the size)
645 *
646 * ```ts import.meta.vitest
647 * import { HashMap } from "effect"
648 *
649 * const emptyMap = HashMap.empty<string, number>()
650 * const map = HashMap.make(["a", 1], ["b", 2], ["c", 3])
651 *
652 * HashMap.size(emptyMap) // => 0
653 * HashMap.size(map) // => 3
654 * ```
655 *
656 * @category getters
657 * @since 2.0.0
658 */
659export const size: <K, V>(self: HashMap<K, V>) => number = internal.size
660
661/**
662 * Creates a transient mutable `HashMap` for efficient batched updates.
663 *
664 * **Details**
665 *
666 * Apply updates to the returned map, then call `endMutation` to finish the
667 * mutation window and use the result as an immutable `HashMap`.
668 *
669 * **Example** (Beginning batch mutation)
670 *
671 * ```ts import.meta.vitest
672 * import { HashMap } from "effect"
673 *
674 * const map = HashMap.make(["a", 1])
675 *
676 * // Begin mutation for efficient batch operations
677 * const mutable = HashMap.beginMutation(map)
678 *
679 * // Multiple operations are now more efficient
680 * HashMap.set(mutable, "b", 2)
681 * HashMap.set(mutable, "c", 3)
682 * HashMap.remove(mutable, "a")
683 *
684 * // End mutation to get final immutable result
685 * HashMap.endMutation(mutable) // => HashMap.make(["b", 2], ["c", 3])
686 * ```
687 *
688 * @category mutations
689 * @since 2.0.0
690 */
691export const beginMutation: <K, V>(self: HashMap<K, V>) => HashMap<K, V> = internal.beginMutation
692
693/**
694 * Marks the `HashMap` as immutable, completing the mutation cycle.
695 *
696 * **Example** (Ending batch mutation)
697 *
698 * ```ts import.meta.vitest
699 * import { HashMap } from "effect"
700 *
701 * // Start with an existing map
702 * const original = HashMap.make(["x", 10], ["y", 20])
703 *
704 * // Begin mutation for batch operations
705 * const mutable = HashMap.beginMutation(original)
706 *
707 * // Perform multiple efficient operations
708 * HashMap.set(mutable, "z", 30)
709 * HashMap.remove(mutable, "x")
710 * HashMap.set(mutable, "w", 40)
711 *
712 * // End mutation to get final immutable result
713 * HashMap.endMutation(mutable) // => HashMap.make(["y", 20], ["z", 30], ["w", 40])
714 * ```
715 *
716 * @category mutations
717 * @since 2.0.0
718 */
719export const endMutation: <K, V>(self: HashMap<K, V>) => HashMap<K, V> = internal.endMutation
720
721/**
722 * Runs a batch of updates against a transient mutable copy of the `HashMap`
723 * and returns the finalized immutable result.
724 *
725 * **Details**
726 *
727 * The callback may call mutation-oriented helpers such as `set` and `remove`
728 * on the transient map.
729 *
730 * **Example** (Applying batched mutations)
731 *
732 * ```ts import.meta.vitest
733 * import { HashMap } from "effect"
734 *
735 * const map1 = HashMap.make(["a", 1])
736 * const map2 = HashMap.mutate(map1, (mutable) => {
737 * HashMap.set(mutable, "b", 2)
738 * HashMap.set(mutable, "c", 3)
739 * })
740 * map2 // => HashMap.make(["a", 1], ["b", 2], ["c", 3])
741 * ```
742 *
743 * @category mutations
744 * @since 2.0.0
745 */
746export const mutate: {
747 <K, V>(f: (self: HashMap<K, V>) => void): (self: HashMap<K, V>) => HashMap<K, V>
748 <K, V>(self: HashMap<K, V>, f: (self: HashMap<K, V>) => void): HashMap<K, V>
749} = internal.mutate
750
751/**
752 * Sets or removes the specified key using an update function.
753 *
754 * **Details**
755 *
756 * The update function receives `Some(value)` when the key exists or `None`
757 * when it does not. Returning `Some(newValue)` stores the value, and returning
758 * `None` removes the key or leaves it absent.
759 *
760 * **Example** (Updating values with Options)
761 *
762 * ```ts import.meta.vitest
763 * import { HashMap, Option } from "effect"
764 *
765 * const map = HashMap.make(["a", 1], ["b", 2])
766 *
767 * // Increment existing value or set to 1 if not present
768 * const updateFn = (option: Option.Option<number>) =>
769 * Option.isSome(option) ? Option.some(option.value + 1) : Option.some(1)
770 *
771 * const updated = HashMap.modifyAt(map, "a", updateFn)
772 * HashMap.get(updated, "a") // => Option.some(2)
773 * ```
774 *
775 * @category transforming
776 * @since 2.0.0
777 */
778export const modifyAt: {
779 <K, V>(key: K, f: HashMap.UpdateFn<V>): (self: HashMap<K, V>) => HashMap<K, V>
780 <K, V>(self: HashMap<K, V>, key: K, f: HashMap.UpdateFn<V>): HashMap<K, V>
781} = internal.modifyAt
782
783/**
784 * Sets or removes the specified key using a precomputed hash and an update
785 * function.
786 *
787 * **Details**
788 *
789 * The update function receives `Some(value)` when the key exists or `None`
790 * when it does not. Returning `Some(newValue)` stores the value, and returning
791 * `None` removes the key or leaves it absent.
792 *
793 * **Example** (Updating values with a hash)
794 *
795 * ```ts import.meta.vitest
796 * import { Hash, HashMap, Option } from "effect"
797 *
798 * // Useful when working with precomputed hashes for performance
799 * const counters = HashMap.make(["downloads", 100], ["views", 250])
800 *
801 * // Cache hash computation for frequently accessed keys
802 * const metricKey = "downloads"
803 * const cachedHash = Hash.string(metricKey)
804 *
805 * // Update function that increments counter or initializes to 1
806 * const incrementCounter = (current: Option.Option<number>) =>
807 * Option.isSome(current) ? Option.some(current.value + 1) : Option.some(1)
808 *
809 * // Use cached hash for efficient updates in loops
810 * const updated = HashMap.modifyHash(
811 * counters,
812 * metricKey,
813 * cachedHash,
814 * incrementCounter
815 * )
816 * HashMap.get(updated, "downloads") // => Option.some(101)
817 *
818 * // Add new metric with precomputed hash
819 * const newMetric = "clicks"
820 * const clicksHash = Hash.string(newMetric)
821 * const withClicks = HashMap.modifyHash(
822 * updated,
823 * newMetric,
824 * clicksHash,
825 * incrementCounter
826 * )
827 * HashMap.get(withClicks, "clicks") // => Option.some(1)
828 * ```
829 *
830 * @category transforming
831 * @since 2.0.0
832 */
833export const modifyHash: {
834 <K, V>(key: K, hash: number, f: HashMap.UpdateFn<V>): (self: HashMap<K, V>) => HashMap<K, V>
835 <K, V>(self: HashMap<K, V>, key: K, hash: number, f: HashMap.UpdateFn<V>): HashMap<K, V>
836} = internal.modifyHash
837
838/**
839 * Updates the value of the specified key within the `HashMap` if it exists.
840 *
841 * **Example** (Modifying existing values)
842 *
843 * ```ts import.meta.vitest
844 * import { HashMap, Option } from "effect"
845 *
846 * const map1 = HashMap.make(["a", 1], ["b", 2])
847 * const map2 = HashMap.modify(map1, "a", (value) => value * 3)
848 *
849 * HashMap.get(map2, "a") // => Option.some(3)
850 * HashMap.get(map2, "b") // => Option.some(2)
851 * ```
852 *
853 * @category transforming
854 * @since 2.0.0
855 */
856export const modify: {
857 <K, V>(key: K, f: (v: V) => V): (self: HashMap<K, V>) => HashMap<K, V>
858 <K, V>(self: HashMap<K, V>, key: K, f: (v: V) => V): HashMap<K, V>
859} = internal.modify
860
861/**
862 * Combines two `HashMap`s into one.
863 *
864 * **Details**
865 *
866 * Entries from `that` are inserted into `self`; when both maps contain an
867 * equal key, the value from `that` replaces the value from `self`.
868 *
869 * **Example** (Combining HashMaps)
870 *
871 * ```ts import.meta.vitest
872 * import { HashMap, Option } from "effect"
873 *
874 * const map1 = HashMap.make(["a", 1], ["b", 2])
875 * const map2 = HashMap.make(["b", 20], ["c", 3])
876 * const union = HashMap.union(map1, map2)
877 *
878 * union // => HashMap.make(["a", 1], ["b", 20], ["c", 3])
879 * HashMap.get(union, "b") // => Option.some(20)
880 * ```
881 *
882 * @category combining
883 * @since 2.0.0
884 */
885export const union: {
886 <K1, V1>(that: HashMap<K1, V1>): <K0, V0>(self: HashMap<K0, V0>) => HashMap<K1 | K0, V1 | V0>
887 <K0, V0, K1, V1>(self: HashMap<K0, V0>, that: HashMap<K1, V1>): HashMap<K0 | K1, V0 | V1>
888} = internal.union
889
890/**
891 * Removes the entry for the specified key in the `HashMap` using the internal
892 * hashing function.
893 *
894 * **Example** (Removing a key)
895 *
896 * ```ts import.meta.vitest
897 * import { HashMap } from "effect"
898 *
899 * const map1 = HashMap.make(["a", 1], ["b", 2], ["c", 3])
900 * const map2 = HashMap.remove(map1, "b")
901 *
902 * map2 // => HashMap.make(["a", 1], ["c", 3])
903 * ```
904 *
905 * @category transforming
906 * @since 2.0.0
907 */
908export const remove: {
909 <K>(key: K): <V>(self: HashMap<K, V>) => HashMap<K, V>
910 <K, V>(self: HashMap<K, V>, key: K): HashMap<K, V>
911} = internal.remove
912
913/**
914 * Removes all entries in the `HashMap` which have the specified keys.
915 *
916 * **Example** (Removing multiple keys)
917 *
918 * ```ts import.meta.vitest
919 * import { HashMap } from "effect"
920 *
921 * const map1 = HashMap.make(["a", 1], ["b", 2], ["c", 3], ["d", 4])
922 * const map2 = HashMap.removeMany(map1, ["b", "d"])
923 *
924 * map2 // => HashMap.make(["a", 1], ["c", 3])
925 * ```
926 *
927 * @category transforming
928 * @since 2.0.0
929 */
930export const removeMany: {
931 <K>(keys: Iterable<K>): <V>(self: HashMap<K, V>) => HashMap<K, V>
932 <K, V>(self: HashMap<K, V>, keys: Iterable<K>): HashMap<K, V>
933} = internal.removeMany
934
935/**
936 * Sets multiple key-value pairs in the `HashMap`.
937 *
938 * **Example** (Setting multiple entries)
939 *
940 * ```ts import.meta.vitest
941 * import { HashMap, Option } from "effect"
942 *
943 * const map1 = HashMap.make(["a", 1], ["b", 2])
944 * const newEntries = [["c", 3], ["d", 4], ["a", 10]] as const // "a" will be overwritten
945 * const map2 = HashMap.setMany(map1, newEntries)
946 *
947 * map2 // => HashMap.make(["a", 10], ["b", 2], ["c", 3], ["d", 4])
948 * HashMap.get(map2, "a") // => Option.some(10)
949 * ```
950 *
951 * @category transforming
952 * @since 4.0.0
953 */
954export const setMany: {
955 <K, V>(entries: Iterable<readonly [K, V]>): (self: HashMap<K, V>) => HashMap<K, V>
956 <K, V>(self: HashMap<K, V>, entries: Iterable<readonly [K, V]>): HashMap<K, V>
957} = internal.setMany
958
959/**
960 * Maps over the entries of the `HashMap` using the specified function.
961 *
962 * **Example** (Mapping values)
963 *
964 * ```ts import.meta.vitest
965 * import { HashMap, Option } from "effect"
966 *
967 * const map1 = HashMap.make(["a", 1], ["b", 2], ["c", 3])
968 * const map2 = HashMap.map(map1, (value, key) => `${key}:${value * 2}`)
969 *
970 * HashMap.get(map2, "a") // => Option.some("a:2")
971 * HashMap.get(map2, "b") // => Option.some("b:4")
972 * ```
973 *
974 * @category mapping
975 * @since 2.0.0
976 */
977export const map: {
978 <A, V, K>(f: (value: V, key: K) => A): (self: HashMap<K, V>) => HashMap<K, A>
979 <K, V, A>(self: HashMap<K, V>, f: (value: V, key: K) => A): HashMap<K, A>
980} = internal.map
981
982/**
983 * Maps each entry to a `HashMap` and flattens the results.
984 *
985 * **Gotchas**
986 *
987 * The hash and equality behavior of both maps have to be the same.
988 *
989 * **Example** (Flat mapping values)
990 *
991 * ```ts import.meta.vitest
992 * import { HashMap, Option } from "effect"
993 *
994 * const map1 = HashMap.make(["a", 1], ["b", 2])
995 * const map2 = HashMap.flatMap(
996 * map1,
997 * (value, key) => HashMap.make([key + "1", value], [key + "2", value * 2])
998 * )
999 *
1000 * map2 // => HashMap.make(["a1", 1], ["a2", 2], ["b1", 2], ["b2", 4])
1001 * HashMap.get(map2, "b2") // => Option.some(4)
1002 * ```
1003 *
1004 * @category sequencing
1005 * @since 2.0.0
1006 */
1007export const flatMap: {
1008 <A, K, B>(f: (value: A, key: K) => HashMap<K, B>): (self: HashMap<K, A>) => HashMap<K, B>
1009 <K, A, B>(self: HashMap<K, A>, f: (value: A, key: K) => HashMap<K, B>): HashMap<K, B>
1010} = internal.flatMap
1011
1012/**
1013 * Applies the specified function to the entries of the `HashMap`.
1014 *
1015 * **Example** (Iterating with side effects)
1016 *
1017 * ```ts import.meta.vitest
1018 * import { HashMap } from "effect"
1019 *
1020 * const map = HashMap.make(["a", 1], ["b", 2])
1021 * const collected: Array<[string, number]> = []
1022 *
1023 * HashMap.forEach(map, (value, key) => {
1024 * collected.push([key, value])
1025 * })
1026 *
1027 * collected.sort() // => [["a", 1], ["b", 2]]
1028 * ```
1029 *
1030 * @category traversing
1031 * @since 2.0.0
1032 */
1033export const forEach: {
1034 <V, K>(f: (value: V, key: K) => void): (self: HashMap<K, V>) => void
1035 <V, K>(self: HashMap<K, V>, f: (value: V, key: K) => void): void
1036} = internal.forEach
1037
1038/**
1039 * Reduces the specified state over the entries of the `HashMap`.
1040 *
1041 * **Example** (Reducing values)
1042 *
1043 * ```ts import.meta.vitest
1044 * import { HashMap } from "effect"
1045 *
1046 * const map = HashMap.make(["a", 1], ["b", 2], ["c", 3])
1047 * HashMap.reduce(map, 0, (acc, value) => acc + value) // => 6
1048 * ```
1049 *
1050 * @category folding
1051 * @since 2.0.0
1052 */
1053export const reduce: {
1054 <Z, V, K>(zero: Z, f: (accumulator: Z, value: V, key: K) => Z): (self: HashMap<K, V>) => Z
1055 <K, V, Z>(self: HashMap<K, V>, zero: Z, f: (accumulator: Z, value: V, key: K) => Z): Z
1056} = internal.reduce
1057
1058/**
1059 * Filters entries out of a `HashMap` using the specified predicate.
1060 *
1061 * **Example** (Filtering entries)
1062 *
1063 * ```ts import.meta.vitest
1064 * import { HashMap } from "effect"
1065 *
1066 * const map1 = HashMap.make(["a", 1], ["b", 2], ["c", 3], ["d", 4])
1067 * const map2 = HashMap.filter(map1, (value) => value % 2 === 0)
1068 *
1069 * map2 // => HashMap.make(["b", 2], ["d", 4])
1070 * ```
1071 *
1072 * @category filtering
1073 * @since 2.0.0
1074 */
1075export const filter: {
1076 <K, A>(f: (a: NoInfer<A>, k: K) => boolean): (self: HashMap<K, A>) => HashMap<K, A>
1077 <K, A>(self: HashMap<K, A>, f: (a: A, k: K) => boolean): HashMap<K, A>
1078} = internal.filter
1079
1080/**
1081 * Filters out `None` values from a `HashMap` of `Options`s.
1082 *
1083 * **Example** (Compacting Option values)
1084 *
1085 * ```ts import.meta.vitest
1086 * import { HashMap, Option } from "effect"
1087 *
1088 * const map1 = HashMap.make(
1089 * ["a", Option.some(1)],
1090 * ["b", Option.none()],
1091 * ["c", Option.some(3)]
1092 * )
1093 * const map2 = HashMap.compact(map1)
1094 *
1095 * map2 // => HashMap.make(["a", 1], ["c", 3])
1096 * HashMap.get(map2, "a") // => Option.some(1)
1097 * ```
1098 *
1099 * @category filtering
1100 * @since 2.0.0
1101 */
1102export const compact: <K, A>(self: HashMap<K, Option<A>>) => HashMap<K, A> = internal.compact
1103
1104/**
1105 * Maps over the entries of the `HashMap` using the specified filter and keeps
1106 * only successful results.
1107 *
1108 * **Example** (Filtering and mapping Results)
1109 *
1110 * ```ts import.meta.vitest
1111 * import { HashMap, Option, Result } from "effect"
1112 *
1113 * const map1 = HashMap.make(["a", 1], ["b", 2], ["c", 3], ["d", 4])
1114 * const map2 = HashMap.filterMap(
1115 * map1,
1116 * (value) => value % 2 === 0 ? Result.succeed(value * 2) : Result.failVoid
1117 * )
1118 *
1119 * map2 // => HashMap.make(["b", 4], ["d", 8])
1120 * HashMap.get(map2, "b") // => Option.some(4)
1121 * ```
1122 *
1123 * @category filtering
1124 * @since 2.0.0
1125 */
1126export const filterMap: {
1127 <A, K, B, X>(f: (input: A, key: K) => Result<B, X>): (self: HashMap<K, A>) => HashMap<K, B>
1128 <K, A, B, X>(self: HashMap<K, A>, f: (input: A, key: K) => Result<B, X>): HashMap<K, B>
1129} = internal.filterMap
1130
1131/**
1132 * Returns the first element that satisfies the specified
1133 * predicate, or `None` if no such element exists.
1134 *
1135 * **Example** (Finding the first matching entry)
1136 *
1137 * ```ts import.meta.vitest
1138 * import { HashMap, Option } from "effect"
1139 *
1140 * const map = HashMap.make(["a", 1], ["b", 2], ["c", 3])
1141 * HashMap.findFirst(map, (value, key) => key === "b" && value > 1) // => Option.some(["b", 2])
1142 * ```
1143 *
1144 * @category searching
1145 * @since 2.0.0
1146 */
1147export const findFirst: {
1148 <K, A>(predicate: (a: NoInfer<A>, k: K) => boolean): (self: HashMap<K, A>) => Option<[K, A]>
1149 <K, A>(self: HashMap<K, A>, predicate: (a: A, k: K) => boolean): Option<[K, A]>
1150} = internal.findFirst
1151
1152/**
1153 * Checks whether any entry in a hashmap meets a specific condition.
1154 *
1155 * **Example** (Checking for any matching entry)
1156 *
1157 * ```ts import.meta.vitest
1158 * import { HashMap } from "effect"
1159 *
1160 * const map = HashMap.make(["a", 1], ["b", 2], ["c", 3])
1161 *
1162 * HashMap.some(map, (value) => value > 2) // => true
1163 * HashMap.some(map, (value) => value > 5) // => false
1164 * ```
1165 *
1166 * @category predicates
1167 * @since 3.13.0
1168 */
1169export const some: {
1170 <K, A>(predicate: (a: NoInfer<A>, k: K) => boolean): (self: HashMap<K, A>) => boolean
1171 <K, A>(self: HashMap<K, A>, predicate: (a: A, k: K) => boolean): boolean
1172} = internal.some
1173
1174/**
1175 * Checks whether all entries in a hashmap meets a specific condition.
1176 *
1177 * **Example** (Checking all entries)
1178 *
1179 * ```ts import.meta.vitest
1180 * import { HashMap } from "effect"
1181 *
1182 * const map = HashMap.make(["a", 1], ["b", 2], ["c", 3])
1183 *
1184 * HashMap.every(map, (value) => value > 0) // => true
1185 * HashMap.every(map, (value) => value > 1) // => false
1186 * ```
1187 *
1188 * @category predicates
1189 * @since 3.14.0
1190 */
1191export const every: {
1192 <K, A>(predicate: (a: NoInfer<A>, k: K) => boolean): (self: HashMap<K, A>) => boolean
1193 <K, A>(self: HashMap<K, A>, predicate: (a: A, k: K) => boolean): boolean
1194} = internal.every