How do EK9 constants compare to const, final, and static in other languages?
← Constants and Immutability · Ref: Q143
Developers frequently confuse const, final, and static because these keywords overlap differently across languages. Here is how each language handles constants and what EK9 does differently.
THE CONFUSION
The word 'constant' should mean 'this value never changes'. But most languages only guarantee 'this binding cannot be reassigned', which is fundamentally different. A final reference to a mutable object does not make the object constant.
JAVA
Java uses 'final' to prevent reassignment, but final does NOT prevent mutation:
final List<String> names = new ArrayList<>(); names.add("Alice"); // This works! The list is mutated.
The 'static final' idiom is Java's convention for constants, but even static final objects can be mutated if they are mutable types. Only primitive static final fields are truly constant (inlined by the compiler).
RUST
Rust's 'const' is truly compile-time: the value must be computable at compile time and is limited to scalar-like types. 'static' is runtime-initialized and lives for the program lifetime. 'let' bindings are immutable by default but interior mutability (RefCell, Mutex) can bypass this. Rust comes closest to real immutability but still has escape hatches.
GO
Go's 'const' is compile-time only and limited to basic types: numbers, strings, and booleans. No structs, slices, or maps. Everything else is a 'var' that you promise not to change, with no compiler enforcement.
KOTLIN
Kotlin's 'const val' is compile-time, limited to primitives and String. 'val' prevents reassignment but not mutation: val list = mutableListOf() still allows list.add(). Similar to Java's final.
JAVASCRIPT
JavaScript's 'const' is arguably the most misleading: it prevents reassignment but not mutation. const obj = {}; obj.x = 1 works perfectly. The name 'const' suggests immutability but delivers only binding fixity.
EK9
EK9's 'defines constant' block provides true value immutability through copy-on-access:
1. Every access returns a fresh independent copy via copy constructor 2. All mutation operators are blocked at compile time (NOT_MUTABLE error) 3. Supports 18 types including domain types (Money, Colour, Duration, Dimension) 4. No escape hatch: you cannot mutate a constant through any mechanism
The key insight is that assigning a constant to a variable gives you a mutable copy. The constant itself is forever protected, but the copy is yours to modify freely.
KEY INSIGHT
Most languages conflate 'immutable binding' with 'immutable value'. EK9 separates them: the constant value is immutable (copy-on-access), while variables that receive copies are fully mutable. This eliminates the confusion entirely.
See Q140 for defining constants in EK9. See Q141 for copy-on-access immutability details. See Q130 for mutable vs immutable collection operations.
Example
defines module qa.constants.comparison defines constant greeting <- "Hello" count <- 42 price <- 9.99#USD colour <- #FF8800 defines program ConstFinalStaticDemo() stdout <- Stdout() // === EK9: CONSTANT ACCESS GIVES YOU A COPY === stdout.println(`Constant: ${greeting}`) stdout.println(`Count: ${count}`) // === VARIABLE FROM CONSTANT IS MUTABLE === name <- greeting name += " World" stdout.println(`Modified copy: ${name}`) // Constant unchanged stdout.println(`Constant still: ${greeting}`) // === EXPRESSIONS WITH CONSTANTS PRODUCE MUTABLE RESULTS === doubled <- count + count stdout.println(`Doubled: ${doubled}`) doubled := doubled + 1 stdout.println(`Incremented copy: ${doubled}`) // Original constant unchanged stdout.println(`Count still: ${count}`) // === DOMAIN TYPE CONSTANTS === stdout.println(`Price: ${price}`) stdout.println(`Colour: ${colour}`) localPrice <- price stdout.println(`Local price copy: ${localPrice}`)
Common mistakes
E07890 — Unlike Java's final which only prevents reassignment, EK9 constants block ALL mutation operators including +=. Assign the constant to a mutable variable first, then modify the copy. See ek9 -h E07890 for details.
Incorrect:
greeting += " World"
Correct:
name <- greeting
E07890 — Constants cannot be reassigned. The := operator mutates the target variable, which is forbidden for constants. Copy the constant to a local variable first. See ek9 -h E07890 for details.
Incorrect:
count := count + 1
Correct:
doubled := doubled + 1
E07890 — The :=? guarded assignment still mutates the target when it is unset. EK9 constants cannot be mutated through any mechanism including guarded assignment. Constants are always set, so :=? would never trigger anyway. See ek9 -h E07890 for details.
Incorrect:
greeting :=? "World"
Correct:
name <- greeting
Other ways to ask this
- What is the difference between const and final?
- How does EK9 const compare to Java final?
- Is EK9 const like Rust const or JavaScript const?
Coming from another language?
Java: final prevents reassignment not mutation, static final is convention for constants. Rust: const is compile-time only (scalar types), let is immutable binding with interior mutability escape. Go: const limited to basic types, no struct/slice/map. Kotlin: const val for primitives/String only, val prevents reassignment not mutation. JavaScript: const prevents reassignment not mutation (most misleading). EK9: copy-on-access true immutability for 18 types, no escape hatch, variable copies are mutable.
Keywords: java, immutable, final, comparison, reassignment, migrate, mutation, go, const, static, constant, rust