How do guard expressions work in EK9 if statements?
← Control Flow · Ref: Q971
Guard expressions combine variable handling with isSet checking in a single statement. The block only executes if the value is SET.
THREE GUARD FORMS:
1. DECLARATION GUARD (<-):
if name <- getName() stdout.println(name) declares name, only runs if SET
2. ASSIGNMENT GUARD (:=):
selectedItem <- String() if selectedItem := findItem("key") stdout.println(selectedItem) assigns to existing var, only runs if SET
3. GUARDED ASSIGNMENT (:=?):
config <- loadDefaults() if config :=? loadOverrides() stdout.println(config) only assigns if loadOverrides() returned SET
The := guard always assigns, then checks isSet. The :=? guard only assigns when the right side is SET — the existing value is preserved if the right side is unset.
WITH ADDITIONAL CONDITION:
if name <- getName() then name.length() > 3 stdout.println(name) runs if SET AND length > 3
All three guard forms work IDENTICALLY across all control flow:
if v <- expr() declaration guard switch v := expr() assignment guard while v :=? expr() guarded assignment try v <- expr() declaration guard in try for v <- iterator declaration guard in for
This eliminates 90-95% of null pointer exceptions through compile-time enforcement.
Bridge: Like Go's 'if err := doSomething(); err != nil'. Like Rust's 'if let Some(v) = expr()'. Like Swift's 'if let v = optionalExpr'.
See Q74 for more guard examples. See Q75 for guard switch.
Example
defines module qa.controlflow.guardif defines function <?- Returns a greeting if the name has content, or unset if empty. -?> findGreeting() as pure -> personName as String <- rtn as String: String() if personName? rtn: "Hello, " + personName defines program GuardIfDemo() stdout <- Stdout() // BASIC GUARD: only enters if findGreeting returns SET if greeting <- findGreeting("Steve") stdout.println(greeting) // GUARD WITH UNSET: block is skipped if greeting <- findGreeting(String()) stdout.println("This won't print") else stdout.println("Greeting was unset - skipped") // GUARD WITH CONDITION: SET and passes additional test minLength <- 3 if greeting <- findGreeting("Alice") then greeting.length() > minLength stdout.println(`Long greeting: ${greeting}`) // GUARD IN SWITCH: declares and checks in one step switch greet <- findGreeting("Charlie") with greet case == "Hello, Charlie" stdout.println("Found Charlie's greeting") default stdout.println(`Other greeting: ${greet}`)
Common mistakes
E01073 — 'null' does not exist in EK9 — use tri-state semantics (unset/set) with guard expressions such as 'if name <- expr()' instead of comparing against null. See ek9 -h E01073 for details.
Incorrect:
greeting.length() > minLength and greeting <> null
Correct:
greeting.length() > minLength
Other ways to ask this
- What is a guard expression in EK9?
- How does if v <- expr() work in EK9?
- How do I combine declaration with null checking in EK9?
- What does the <- guard do inside an if statement?
Coming from another language?
Go: if err := f(); err != nil { } — same concept. Rust: if let Some(v) = expr { } — same concept. Swift: if let v = optional { } — same concept. Java: requires separate null check. EK9 guards are universal across all control flow.
Keywords: guard, null safety, isSet check, control flow, declaration guard