Control flow
if
The if statement is pretty much what you would expect.
// The usual
if (someCondition) {
// ...
} else if (anotherCondition) {
// ...
} else {
// ...
}
You can unwrap an optional with the -> operator.
if (opt -> unwrapped) {
std\print(unwrapped);
}
You can downcast a value with the as operator.
final anything: any = "hello";
//...
if (anything as something: str) {
std\print(something);
}
if can also be inlined and act as an expression. However this expression does not support null unwrapping or downcasting (those are available in expressions using ?? and as?).
final value = if (something > 0) 12 else 13;
match
match selects the first branch whose condition matches the value. Each branch can list one or more conditions separated by commas.
final category = match (code) {
200 -> "ok",
400, 404 -> "client error",
// code is within this range
500..599 -> "server error",
else -> "unknown",
};
It can also be used as a statement.
match (state) {
"ready" -> std\print("starting"),
"done" -> std\print("already finished"),
else -> std\print("waiting"),
}
A statement branch can use a lexical blocks:
var result = "unset";
match (input) {
$"hello [a-z]+" -> {
final branch = "pattern";
result = branch;
},
else -> {
result = "unknown";
},
}
How conditions are matched depends on the matched value:
- When matching a
str, astrcondition uses simple equality and apatcondition matches the string against the pattern. - When matching a
pat, apatcondition uses simple equality and astrcondition matches the pattern against the string. - When matching a number, a numeric condition uses simple equality and a
rgcondition matches when the number is contained in the range. - When matching a
type, atypecondition uses simple equality. Any other condition matches when the condition value is of the matched type. - When matching any other value, a
typecondition is checked asvalue is Type; other conditions use simple equality.
For example:
final greeting = match ("hello joe") {
$"hello [a-z]+" -> "friendly",
else -> "unknown",
};
final value: any = 42;
final kind = match (value) {
<str> -> "string",
<int> -> "integer",
else -> "other",
};
match must be exhaustive. Use an else branch unless the compiler can prove that all possible cases are covered, such as every value of a boolean or every case of an enum.
enum Status {
draft,
published,
}
final status = Status.published;
final action = match (status) {
.draft -> "edit",
.published -> "read",
};
while and do .. until
Repeat a block of statements while or until a condition is true.
var i = 0;
while (i < 10) {
i = i + 1;
}
var j = 10;
do {
j = j - 1;
} until (j == 0)
for
for is pretty much the same statement as in C.
for (i: int = 0; i < 10; i = i + 1) {
// ...
break;
}
foreach
foreach can iterate over most data structures. The key/index variable can be omitted.
foreach (case in SomeEnum) {
// ...
}
foreach (i, value in listOfStrings) {
// ...
}
foreach (key, value in aMap) {
// ...
}
foreach (i, char in aString) {
// ...
}
final fibonacciFib = &fibonacci(10);
foreach (value in fibonacciFib) {
// ...
}
// The key can be omitted
foreach (char in aString) {
// ...
}
foreach (i in 0..n) {
// ...
}
break and continue
break will stop a loop.
while (true) {
if (condition) {
break;
}
}
continue will skip any following statement and start the loop again.
while (true) {
if (condition) {
continue;
}
std\print("not reached if `condition` is true");
}
You can add a label to any loop and break/continue to it. This is useful when you have nested loops and want to break to an upper scope:
while (true) :here {
if (condition) {
break here;
}
std\print("not reached if `condition` is true");
}
Block expression
Block expressions are lexical blocks that produce a value:
final value = from {
if (flag) {
out 1;
}
out 2;
};
out exits the nearest block expression immediately. It can appear in branches, but if any reachable path uses out, every reachable path must end with out or another terminal statement such as return or throw.
