Immutable Maps (ImmMap) in Phunkie are key-value collections that cannot be modified after creation. They provide a type-safe, functional way to work with associative data structures.
ImmMap implements several type class interfaces:
There are several ways to create immutable maps:
// Empty map
$empty = ImmMap();
// From associative array
$map = ImmMap(["hello" => "there"]);
// From key-value pairs
$map = ImmMap(
"key1", "value1",
"key2", "value2"
);
// Using objects as keys
$map = ImmMap(
new AccountNumber(1), new Account("John Smith"),
new AccountNumber(2), new Account("Chuck Norris")
);
Maps provide several basic operations for accessing and querying data:
$map = ImmMap(["hello" => "there", "hi" => "here"]);
// Get value (returns Option)
$map->get("hello"); // Some("there")
$map->get("missing"); // None
// Check if key exists
$map->contains("hello"); // true
// Get with default value
$map->getOrElse("hello", "default"); // "there"
$map->getOrElse("missing", "default"); // "default"
// Get all keys
$map->keys(); // ["hello", "hi"]
// Get all values
$map->values(); // ["there", "here"]
// Check if empty
$map->isEmpty(); // false
ImmMap()->isEmpty(); // true
// Get size
$map->size(); // 2
Maps provide operations that return new maps without modifying the original:
$map = ImmMap(["hello" => "there", "hi" => "here"]);
// Add or update entry
$newMap = $map->plus("hey", "hello");
$updated = $map->updated("hello", "world"); // Alias for plus
// Remove entry
$newMap = $map->minus("hi");
$removed = $map->removed("hi"); // Alias for minus
// Copy map
$copy = $map->copy();
// Copy with updates
$modified = $map->copy(["hi" => "world"]);
ImmMap implements the Functor type class, allowing you to map over key-value pairs:
$map = ImmMap(["a" => 1, "b" => 2, "c" => 3]);
// Map over key-value pairs (receives Pair)
$result = $map->map(fn(Pair $kv) => Pair($kv->_1, $kv->_2 + 1));
// ImmMap(["a" => 2, "b" => 3, "c" => 4])
// Map over values only
$result = $map->mapValues(fn($v) => $v * 2);
// ImmMap(["a" => 2, "b" => 4, "c" => 6])
// Map over keys only
$result = ImmMap([1 => "a", 2 => "b"])->mapKeys(fn($k) => "key_" . $k);
// ImmMap(["key_1" => "a", "key_2" => "b"])
// Map with both key and value
$result = $map->mapWithKey(fn($k, $v) => "$k:$v");
// ImmMap(["a" => "a:1", "b" => "b:2", "c" => "c:3"])
// Transform all values to a single value
$result = $map->as(Pair("a", 0));
// ImmMap(["a" => 0])
// Transform all values using wildcard
$result = $map->as(Pair(_, 0));
// ImmMap(["a" => 0, "b" => 0, "c" => 0])
// Convert all values to Unit
$result = $map->void();
// ImmMap(["a" => Unit(), "b" => Unit(), "c" => Unit()])
// Zip values with their transformation
$result = $map->zipWith(fn($x) => $x * 2);
// ImmMap(["a" => Pair(1, 2), "b" => Pair(2, 4), "c" => Pair(3, 6)])
ImmMap implements Foldable, allowing you to reduce maps to single values:
$map = ImmMap(["a" => 1, "b" => 2, "c" => 3]);
// Fold left (left-to-right)
$sum = $map->foldLeft(0)(fn($acc, $pair) => $acc + $pair->_2);
// 6
// Fold right (right-to-left)
$sum = $map->foldRight(0)(fn($pair, $acc) => $acc + $pair->_2);
// 6
// Fold with initial value
$result = $map->fold(0)(fn($acc, $pair) => $acc + $pair->_2);
// 6
// Fold map - map elements to a monoid and combine
$lists = $map->foldMap(fn($pair) => ImmList($pair->_2));
// ImmList(1, 2, 3)
ImmMap implements Traverse for sequencing effects:
// Sequence - turn a map of Options into an Option of map
$map = ImmMap(["a" => Some(1), "b" => Some(2)]);
$result = $map->sequence();
// Some(ImmMap(["a" => 1, "b" => 2]))
$map2 = ImmMap(["a" => Some(1), "b" => None()]);
$result2 = $map2->sequence();
// None()
// Traverse - map and sequence in one operation
$map = ImmMap(["a" => 1, "b" => 2]);
$result = $map->traverse(fn($pair) => Some($pair->_2 * 2));
// Some(ImmMap(["a" => 2, "b" => 4]))
Filter maps based on predicates:
$map = ImmMap(["a" => 1, "b" => 2, "c" => 3, "d" => 4]);
// Filter by key-value pair
$evens = $map->filter(fn($pair) => $pair->_2 % 2 === 0);
// ImmMap(["b" => 2, "d" => 4])
// Filter by keys only
$result = $map->filterKeys(fn($k) => $k !== "b");
// ImmMap(["a" => 1, "c" => 3, "d" => 4])
// Filter by values only
$result = $map->filterValues(fn($v) => $v > 2);
// ImmMap(["c" => 3, "d" => 4])
ImmMap has monoid operations for combining maps:
$map1 = ImmMap(["a" => 1, "b" => 2]);
$map2 = ImmMap(["b" => 3, "c" => 4]);
// Identity element (empty map)
$empty = $map1->zero();
// ImmMap()
// Combine maps (right map values take precedence)
$combined = $map1->combine($map2);
// ImmMap(["a" => 1, "b" => 3, "c" => 4])
// Monoid laws
$map1->zero()->combine($map1)->eqv($map1); // true (left identity)
$map1->combine($map1->zero())->eqv($map1); // true (right identity)
Map a function that returns a map, then flatten:
$map = ImmMap(["a" => 1, "b" => 2]);
$result = $map->flatMap(fn($pair) => ImmMap([
$pair->_1 . "1" => $pair->_2,
$pair->_1 . "2" => $pair->_2 * 2
]));
// ImmMap(["a1" => 1, "a2" => 2, "b1" => 2, "b2" => 4])
Combine two maps by matching keys:
$map1 = ImmMap(["a" => 1, "b" => 2, "c" => 3]);
$map2 = ImmMap(["a" => "x", "b" => "y", "d" => "z"]);
// Zip into pairs
$result = $map1->zip($map2);
// ImmMap(["a" => Pair(1, "x"), "b" => Pair(2, "y")])
// Zip with a combining function
$result = $map1->zipWithMap($map2, fn($v1, $v2) => "$v1-$v2");
// ImmMap(["a" => "1-x", "b" => "2-y"])
Split a map based on a predicate:
$map = ImmMap(["a" => 1, "b" => 2, "c" => 3, "d" => 4]);
$partitioned = $map->partition(fn($pair) => $pair->_2 % 2 === 0);
// Pair(ImmMap(["b" => 2, "d" => 4]), ImmMap(["a" => 1, "c" => 3]))
$evens = $partitioned->_1; // ImmMap(["b" => 2, "d" => 4])
$odds = $partitioned->_2; // ImmMap(["a" => 1, "c" => 3])
Group map entries by a computed key:
$map = ImmMap(["a" => 1, "b" => 2, "c" => 1, "d" => 3]);
$grouped = $map->groupBy(fn($pair) => $pair->_2);
// ImmMap(
// 1 => ImmList(Pair("a", 1), Pair("c", 1)),
// 2 => ImmList(Pair("b", 2)),
// 3 => ImmList(Pair("d", 3))
// )
Convert map to a list of key-value pairs:
$map = ImmMap(["a" => 1, "b" => 2]);
$list = $map->toList();
// ImmList(Pair("a", 1), Pair("b", 2))
Maps in Phunkie maintain type information for both keys and values:
// Type information is preserved
$map = ImmMap(["a" => 1]);
$map->getTypeVariables(); // [String, Integer]
$map->getTypeArity(); // 2
Maps have a clear string representation for debugging:
$map = ImmMap(["hi" => "here", "hello" => "there"]);
echo $map->toString(); // Map("hi" -> "here", "hello" -> "there")
get() for safe value accessmapValues() when you only need to transform values (more efficient than map())sequence() when working with maps of effects (Option, Either, etc.)getOrElse() when you need a default valueSplObjectStorage internally for efficient storagePair<K,V> objects| Feature | ImmMap | ImmList | ImmSet |
|---|---|---|---|
| Key-value pairs | Yes | No | No |
| Ordered | By insertion | Yes | No |
| Duplicates | No (keys) | Yes | No |
| Lookup | O(1) | O(n) | O(1) |
| Best for | Dictionaries | Sequences | Unique items |