What is the consistent safe-access pattern across all EK9 types?
← Safe Value Access · Ref: Q166
EK9 provides a consistent safe-access pattern across Dict, List, Optional, and Result. The same mental model applies everywhere: provide a default, get a usable value.
DICT: GETORDEFAULT WITH KEY
dict.getOrDefault(key, default)
Lookup by key with fallback.
LIST: GETORDEFAULT WITH INDEX
list.getOrDefault(index, default)
Lookup by position with fallback.
OPTIONAL: GETORDEFAULT
optional.getOrDefault(default)
Extract contained value or use fallback.
RESULT: OKORDEFAULT / ERRORORDEFAULT
result.okOrDefault(default) result.errorOrDefault(default)
Extract ok or error value with fallback.
GUARD PATTERN
All types support the ? operator for guard checks:
if dict contains key then access if opt? then opt.get() if result.isOk() then result.ok()
WHY CONSISTENCY MATTERS
One pattern to learn, works everywhere. No need to remember different APIs for different container types. New developers learn the pattern once and apply it across the entire language.
See Q161 for Dict safe access. See Q162 for List safe access. See Q163 for Optional unwrap. See Q164 for Result extraction. See Q165 for why there is no .get() method.
Example
defines module qa.safeaccess.consistentpattern defines program ConsistentPatternDemo() stdout <- Stdout() // === DICT: getOrDefault(key, default) === config <- {"host": "localhost", "port": "8080"} host <- config.getOrDefault("host", "127.0.0.1") timeout <- config.getOrDefault("timeout", "30") stdout.println(`Host: ${host}`) stdout.println(`Timeout (default): ${timeout}`) // === LIST: getOrDefault(index, default) === args <- ["run", "test", "verbose"] command <- args.getOrDefault(0, "help") missing <- args.getOrDefault(10, "none") stdout.println(`Command: ${command}`) stdout.println(`Missing arg: ${missing}`) // === OPTIONAL: getOrDefault(default) === userName <- Optional("Steve") displayName <- userName.getOrDefault("Anonymous") stdout.println(`User: ${displayName}`) noUser <- Optional() of String fallbackName <- noUser.getOrDefault("Anonymous") stdout.println(`No user: ${fallbackName}`) // === RESULT: okOrDefault(default) === okResult <- Result("Data loaded", Integer()) msg <- okResult.okOrDefault("No data") stdout.println(`Result msg: ${msg}`) errResult <- Result(String(), 404) errMsg <- errResult.okOrDefault("No data") stdout.println(`Error result msg: ${errMsg}`) errCode <- errResult.errorOrDefault(0) stdout.println(`Error code: ${errCode}`)
Common mistakes
E50060 — Dict does not have a .get() method. Use getOrDefault() with a fallback value. Calling a non-existent method triggers E50060 — method not resolved. See ek9 -h E50060 for details.
Incorrect:
host <- config.get("host")
Correct:
host <- config.getOrDefault("host", "127.0.0.1")
E08030 — Optional.get() 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:
displayName <- userName.get()
Correct:
displayName <- userName.getOrDefault("Anonymous")
Other ways to ask this
- How do I use the same safe access pattern on Dict, List, Optional, and Result?
- Is there a unified way to safely access values in EK9?
- How does getOrDefault work across different EK9 types?
Coming from another language?
Java: different APIs per type (Map.get, List.get, Optional.orElse, no Result). Python: dict.get(), list[i] (throws), no Optional. Rust: HashMap.get() returns Option, Vec.get() returns Option, unified Option/Result but different APIs. Go: comma-ok for maps, panic for slices, no Optional/Result. Kotlin: different null-safe operators per context. EK9: getOrDefault() everywhere, one pattern for all container types.
Keywords: access, guard, getOrDefault, error, safe, pattern, consistent, unified, null-safe, dict, isset, absent, ok, result, list, optional