Control Flow in Hare
Learn conditionals, switch statements, and loops in Hare - if/else expressions, switch, for loops, and labeled break/continue with Docker-ready examples
Control flow is how a program decides what to do next — branching on conditions and repeating work. As an imperative systems language in the C tradition, Hare uses familiar structured constructs: if/else for branching, switch for multi-way dispatch, and a single, flexible for keyword that covers every kind of loop.
What sets Hare apart from C is that if is an expression, not just a statement. It evaluates to a value, which means Hare doesn’t need a separate ternary operator. Combined with the requirement that every branch be explicit and the consistent use of braces, Hare’s control flow is conservative and predictable by design — there are no surprises hiding behind implicit conversions or fall-through.
In this tutorial you’ll learn how to branch with if/else if/else, use if as an expression, dispatch on values with switch, write counting and condition-based loops with for, and control loop execution with break and continue — including labeled jumps out of nested loops.
Conditionals: if / else if / else
The most basic control structure is the if statement. The condition must be wrapped in parentheses, and each branch uses braces. Hare does not allow a non-boolean value to stand in as a condition — the expression must be of type bool.
Create a file named conditionals.ha:
| |
Each branch is checked in order, and the first matching one runs. Note the semicolon after the closing brace of the whole if chain — in Hare, control flow constructs are expressions, and statements are terminated with ;.
if as an Expression
Because if evaluates to a value, you can assign its result directly to a binding. This replaces the ternary operator (?:) found in C — Hare deliberately omits the ternary because expression-style if does the same job more readably. Both branches must produce a value of the same type.
Create a file named if_expression.ha:
| |
When used as an expression, the branches can be single expressions without braces, as shown here. An if used this way must have an else branch, since the expression always needs a value to evaluate to.
Switch Statements
When you need to dispatch on the value of a single expression, switch is clearer than a long if/else if chain. Each case lists one or more values, and there is no fall-through — only the matching case runs. The bare case => acts as the default, catching any value not listed.
Create a file named switch.ha:
| |
A switch must be exhaustive: every possible value has to be handled, which is why the default case => is included. The case 4, 5 => line shows how a single branch can match multiple values. Like if, switch is also an expression and can produce a value when every branch returns one.
Loops with for
Hare has a single loop keyword, for, that covers all looping needs. The most common form is the C-style loop with three clauses — an initializer, a condition, and an afterthought. Dropping the initializer and afterthought turns it into a while-style loop driven only by a condition.
Create a file named loops.ha:
| |
The variable i declared with let in the loop header is scoped to the loop body. The while-style form for (n > 0) keeps running as long as the condition holds. An infinite loop is simply for (true) { ... }, which you then exit with break.
Loop Control: break, continue, and Labels
Inside a loop, continue skips to the next iteration and break exits the loop entirely. For nested loops, Hare lets you label a loop and then break or continue a specific outer loop by name — avoiding the flag variables you’d otherwise need.
Create a file named loop_control.ha:
| |
The label :outer is attached to the outer loop, and break :outer jumps out of both loops at once when the condition is met. Without the label, break would only exit the inner loop. The same labeling works with continue :outer to skip to the next iteration of an outer loop.
Running with Docker
Since there is no dedicated Hare image on Docker Hub, we use Alpine Linux’s edge repository, which packages the Hare toolchain. The apk add step installs the compiler before running each program.
| |
If you have Hare installed locally on a supported platform (Linux or a BSD), you can skip the container and run each file directly:
| |
Expected Output
Running conditionals.ha:
Grade: B
Running if_expression.ha:
max = 27, min = 12
12 is even
Running switch.ha:
Wednesday
Running loops.ha:
Counting up:
i = 1
i = 2
i = 3
i = 4
i = 5
Counting down:
n = 3
n = 2
n = 1
Running loop_control.ha:
Odd numbers under 10:
1
3
5
7
9
First pair where i * j > 6:
i = 3, j = 3
Key Concepts
ifis an expression - It evaluates to a value, so Hare has no separate ternary operator; useconst max = if (a > b) a else b;- Conditions must be boolean - Hare won’t accept a non-
boolvalue as a condition, eliminating a common class of C bugs - Semicolons terminate statements - Even after a closing brace, control flow constructs end with
;because they are expressions switchhas no fall-through - Only the matching case runs, and switches must be exhaustive (use the barecase =>as a default)- A case can match multiple values - List them comma-separated, as in
case 4, 5 => - One loop keyword, several forms -
for (init; cond; step)for counting,for (cond)for while-style, andfor (true)for infinite loops - Labeled loop control - Attach a label like
:outerto a loop, thenbreak :outerorcontinue :outerto control nested loops directly
Next Steps
Continue to Functions to learn how to define and call functions in Hare, work with parameters and return values, and use recursion.
Comments
Loading comments...
Leave a Comment