# Control Flow Deft's control-flow forms sit naturally beside bare function calls (see [Syntax and Style](syntax-and-style)). This page covers conditionals, loops, iteration over collections, and `defer` for cleanup. ## `if` / `else` ### Single-line form (preferred for simple conditions)
 if ($n == 0) { return 1 }
if logged-in? { set msg "Welcome!" }
set msg [if admin? "Welcome, admin!" "Welcome, user!"] 
### Multi-line form (complex logic)
 if logged-in? {
    "Welcome!"
} else {
    if admin? {
        "Welcome, admin!"
    } else {
        "Welcome, user!"
    }
} 
### Conditions Conditions are **infix expressions in parentheses** — or a bare truthy/falsy value. Bare parens `(...)` are NOT function calls (see [Syntax and Style](syntax-and-style)).
 if ($count > 10) { ... }                          # ✓ infix condition
if logged-in? { ... }                              # ✓ bare predicate
if $user~active { ... }                            # ✓ field access

if [== $n 0] { ... }                              # ✗ invalid — `==` is infix-only, use ($n == 0) 
### Truthiness `nil` and `false` are falsy. Everything else (including `0`, `""`, `@{}`, `%{}`) is truthy.
 if "" { echo "truthy" }         # prints: truthy
if 0 { echo "truthy" }          # prints: truthy
if nil { echo "nope" }          # no output 
## `match` Pattern matching dispatches on shape, type, and contents in one expression. See [Pattern Matching](pattern-matching) for the complete reference.
 def classify {|n^number|
    match $n {
        0              => "zero"
        1 .. 9         => "single digit"
        $x when ($x < 0) => "negative"
        _              => "positive"
    }
} 
## `for` — Loop Over a Collection `for x in $xs { ... }` iterates a list, map, set, string, or any `ISeq`:
 for x in @{ 1 2 3 } {
    echo $x
}
# 1
# 2
# 3

for entry in %{ :a 1 :b 2 } {
    echo "$entry~0 -> $entry~1"            # map yields @{key value} pairs
}
# a -> 1
# b -> 2

for ch in "abc" { echo $ch }                # string yields single chars 
### `break` and `continue`
 for x in @{ 1 2 3 4 5 } {
    if ($x == 2) { continue }
    if ($x == 4) { break }
    echo $x
}
# 1
# 3 
### Index binding
 for x idx in $xs {
    echo "[$idx] $x"
} 
### `range` for numeric ranges
 for i [range 0 10] { echo $i }              # 0..9
for i [range 1 10 2] { echo $i }            # 1 3 5 7 9 
## `while` — Loop With a Condition
 set n 0
while ($n < 5) {
    echo $n
    incr n
} 
`while` works best for unbounded iteration (parsing input, polling, backoff). For collection iteration, prefer `for`. ## `each` — Side-Effecting Walk `each` is the functional form of `for`. Use it inside pipelines:
 @{ 1 2 3 } |> each { echo $it }

%{ :a 1 :b 2 } |> each { echo "$it~0 = $it~1" } 
Returns `nil`. Don't confuse it with `map` (which produces a lazy seq). ## `defer` — Cleanup on Exit `defer` schedules a block to run when the enclosing function returns, whether normally or via an early `return`/thrown error. Defers run in last-in-first-out order.
 def with-file {|path^string|
    set fh [fs/open $path "r"]
    defer { fs/close $fh }

    # ... use $fh ...
    # fs/close runs when the function exits, however it exits.
} 
### LIFO ordering
 def ordered {|
    defer { echo "first" }
    defer { echo "second" }
    defer { echo "third" }
    "done"
|
}

ordered                  # prints: third, second, first 
### Early-return execution
 def parse-config {|path^string|
    set raw [fs/read $path]
    defer { echo "parsed $path" }            # runs even on early return
    if (empty? $raw) { return %{} }
    [json/parse $raw]
} 
### Scope independence A `defer` inside a nested `def` only fires when that nested function returns — defers are tied to the function frame, not the block:
 def outer {|
    defer { echo "outer exits" }
    def inner {||
        defer { echo "inner exits" }
        "inner"
    |
    }
    [inner]
    "outer"
|
}
# inner exits
# outer exits 
See also [Concurrency](concurrency) for `defer`'s role around async resources. ## `try` / `catch` — Error Handling
 try {
    set result [fs/read $path]
    [json/parse $result]
} catch e {
    echo "failed: $e~message"
    %{}
} 
The caught value is a tagged `%Err{ :message ... }` map. See [Error Handling](error-handling) for the full Result story including `ok`/`err`/`?`/`unwrap`. ## `throw` — Explicit Error
 def divide {|a b|
    if ($b == 0) { [throw "division by zero"] }
    ($a / $b)
} 
The thrown value becomes the `%Err{ :message ... }` payload caught by `try`. ## Early `return` `return` exits the enclosing function immediately with the given value:
 def find-first-positive {|xs^list|
    for x in $xs {
        if ($x > 0) { return $x }
    }
    nil
} 
`return` inside a closure body inside a function still returns from the enclosing **function**, not the closure — closures don't introduce new return frames. ## Quick Reference | Form | Use | |---|---| | `if cond { ... } else { ... }` | Conditional | | `match expr { pattern => result ... }` | Shape-based dispatch | | `for x in $xs { ... }` | Walk a collection | | `for x idx in $xs { ... }` | Walk with index | | `while cond { ... }` | Condition-controlled loop | | `each` | Side-effecting functional walk (in pipelines) | | `defer { ... }` | Schedule cleanup | | `try { ... } catch e { ... }` | Error handling | | `return value` | Early exit | | `break` / `continue` | Loop control | | `[compile-when cond body]` | Compile-time conditional — elides `body` if `cond` falsy (see [Macros](10-macros)) |