Context
NFun's Set/Map currently require the element/key type to be Immutable (renamed from Hashable to capture the underlying property, not the derived capability — see Specs/Collections.md §LINQ-typeclasses).
The initial implementation accepts primitives only:
- ✓
bool, all integers (U8..I64), Real, Char, Text, IPAddress
- ✗ everything else, including types that are conceptually safe
This issue tracks extending the predicate to its full scope as agreed in the design discussion.
Deferred work
Extend Immutable<T> predicate to be recursive over structurally-immutable composites:
Types to add
-
Fun(...) — closure is reference-immutable. Identity hash (memory address). Two distinct rule it+1 instances are different keys; the same lambda reference is the same key. Mirrors Python/JS/Java/C# function-key semantics.
-
FixedArray<T> where Immutable(T) — fixedArray is shallow immutable (slots can't be reassigned). Recursive: fixedArray<list<int>> is NOT immutable because the inner list mutates. fixedArray<int> is immutable. fixedArray<fixedArray<int>> is immutable.
-
ee-mode T[] where Immutable(T) — same as FixedArray, just the legacy ee-mode container.
-
Optional<T> where Immutable(T) — None has trivial hash; Some(x) hashes through x.
-
Future immutable struct / record where all fields are Immutable — when record point = {x:int, y:int} (or frozen struct) lands, it automatically participates.
Always NOT Immutable
List<T> (mutable container)
Array<T> lang-mode (mutable)
Set<T>, Map<K,V> (mutable)
- Mutable struct (lang-mode default)
The rule is transitive immutability: container immutable AND all type parameters Immutable.
Why deferred
Primitive-only Immutable unblocks the common case (Set/Map keyed by int/text/etc.) with minimal TIC integration. The recursive predicate requires:
- Recursive constraint propagation through composite element nodes
- Error messages that say which inner type failed Immutable (e.g.
set<fixedArray<list<int>>> — fail at the inner list)
- Update tests for nested cases
This is a focused 3-5 day task once the primitive Immutable lands.
Convert path users need
When user has a mutable collection and wants to use it as a key:
xs = list(1, 2, 3)
m = map(xs.toFixedArray() -> ""foo"") # explicit freeze
For nested:
xs = list(list(1,2), list(3,4))
key = xs.map(rule it.toFixedArray()).toFixedArray() # freeze recursively
m = map(key -> ""foo"")
When the recursive predicate lands, the error message should suggest this conversion at the user level.
Related
- Design discussion: spec D6 (Hashable → Immutable rename)
- Spec D7 (kind-deferred literals — separate task, will let
map([1,2,3] -> ""foo"") auto-resolve to fixedArray)
- Initial primitive-only implementation: closes
Stage4_Set_RejectsUnhashableElement_Function test
Context
NFun's Set/Map currently require the element/key type to be Immutable (renamed from Hashable to capture the underlying property, not the derived capability — see Specs/Collections.md §LINQ-typeclasses).
The initial implementation accepts primitives only:
bool, all integers (U8..I64),Real,Char,Text,IPAddressThis issue tracks extending the predicate to its full scope as agreed in the design discussion.
Deferred work
Extend
Immutable<T>predicate to be recursive over structurally-immutable composites:Types to add
Fun(...)— closure is reference-immutable. Identity hash (memory address). Two distinctrule it+1instances are different keys; the same lambda reference is the same key. Mirrors Python/JS/Java/C# function-key semantics.FixedArray<T>whereImmutable(T)— fixedArray is shallow immutable (slots can't be reassigned). Recursive:fixedArray<list<int>>is NOT immutable because the inner list mutates.fixedArray<int>is immutable.fixedArray<fixedArray<int>>is immutable.ee-mode T[]whereImmutable(T)— same as FixedArray, just the legacy ee-mode container.Optional<T>whereImmutable(T)— None has trivial hash; Some(x) hashes through x.Future immutable struct / record where all fields are Immutable — when
record point = {x:int, y:int}(orfrozen struct) lands, it automatically participates.Always NOT Immutable
List<T>(mutable container)Array<T>lang-mode (mutable)Set<T>,Map<K,V>(mutable)The rule is transitive immutability: container immutable AND all type parameters Immutable.
Why deferred
Primitive-only Immutable unblocks the common case (Set/Map keyed by int/text/etc.) with minimal TIC integration. The recursive predicate requires:
set<fixedArray<list<int>>>— fail at the innerlist)This is a focused 3-5 day task once the primitive Immutable lands.
Convert path users need
When user has a mutable collection and wants to use it as a key:
For nested:
When the recursive predicate lands, the error message should suggest this conversion at the user level.
Related
map([1,2,3] -> ""foo"")auto-resolve to fixedArray)Stage4_Set_RejectsUnhashableElement_Functiontest