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:
var value = from {
// Doing some work here...
out result;
}
out must appear once at the end of the block.
