Why don't Dict and List have a .get() method in EK9?
← Safe Value Access · Ref: Q165
EK9 deliberately omits .get() on Dict and List because it forces a fallback value, eliminating null returns entirely. This is a design philosophy choice, not an oversight.
THE PROBLEM WITH GET
In Java, map.get(key) returns null if the key is missing. This creates silent null propagation: the null travels through your code until it eventually causes a NullPointerException far from the original lookup. In Python, dict[key] throws KeyError. In Go, accessing a missing map key returns the zero value with no indication it was missing.
THE GETORDEFAULT SOLUTION
By requiring a default value, EK9 ensures every lookup returns a meaningful, usable result:
ages.getOrDefault("Alice", 0)
You must think about the missing-key case at the point of access. The compiler cannot let you forget.
CONSISTENT ACROSS ALL TYPES
The same pattern works on Dict, List, Optional, and Result:
dict.getOrDefault(key, default) list.getOrDefault(index, default) optional.getOrDefault(default) result.okOrDefault(default)
One mental model for safe access everywhere.
See Q46 for Dict basics. See Q45 for List basics. See Q47 for Optional. See Q48 for Result. See Q166 for the consistent safe-access pattern. See Q168 for fallback values. See Q169 for callbacks.
Example
defines module qa.safeaccess.designphilosophy defines program WhyNoGetDemo() stdout <- Stdout() // === THE GETORDEFAULT APPROACH === // Dict: always provide a fallback ages <- {"Alice": 30, "Bob": 25} aliceAge <- ages.getOrDefault("Alice", 0) stdout.println(`Alice: ${aliceAge}`) unknownAge <- ages.getOrDefault("Unknown", 0) stdout.println(`Unknown (default 0): ${unknownAge}`) // List: always provide a fallback numbers <- [10, 20, 30] first <- numbers.getOrDefault(0, -1) stdout.println(`First: ${first}`) outOfBounds <- numbers.getOrDefault(99, -1) stdout.println(`Out of bounds (default -1): ${outOfBounds}`) // Optional: always provide a fallback opt <- Optional("Hello") optVal <- opt.getOrDefault("default") stdout.println(`Optional: ${optVal}`) emptyOpt <- Optional() of String emptyVal <- emptyOpt.getOrDefault("default") stdout.println(`Empty optional: ${emptyVal}`) // Result: always provide a fallback okResult <- Result("Success", Integer()) okVal <- okResult.okOrDefault("fallback") stdout.println(`Result ok: ${okVal}`) errResult <- Result(String(), 42) errOk <- errResult.okOrDefault("fallback") stdout.println(`Error result okOrDefault: ${errOk}`)
Common mistakes
E50060 — Dict does not have a .get() method — only getOrDefault(). Calling .get() triggers E50060 — method not resolved. This is by design: getOrDefault forces you to handle the missing-key case at the point of access. See ek9 -h E50060 for details.
Incorrect:
aliceAge <- ages.get("Alice")
Correct:
aliceAge <- ages.getOrDefault("Alice", 0)
E08030 — Optional does have .get() but it requires a ? guard check first. Calling .get() without checking triggers E08030 — has not been checked before access. Use getOrDefault() for guard-free access. See ek9 -h E08030 for details.
Incorrect:
optVal <- opt.get()
Correct:
optVal <- opt.getOrDefault("default")
Other ways to ask this
- Why does EK9 force me to use getOrDefault instead of get?
- Why is there no .get() on Dict or List in EK9?
- What is the design philosophy behind getOrDefault in EK9?
Coming from another language?
Java: map.get() returns null (NPE risk), map.getOrDefault() added in Java 8 but optional. Python: dict[key] throws KeyError, dict.get(key, default) is the safe form. Rust: HashMap.get() returns Option, forces handling. Go: map[key] returns zero value silently, comma-ok idiom for detection. JavaScript: obj[key] returns undefined, no built-in safe access. EK9: no .get() exists, getOrDefault() is the only access method, forces explicit handling of missing keys.
Keywords: function, access, safe, list, getOrDefault, null, missing, philosophy, null-safe, dict, guard, design, crash, why, exception, get, migrate