Conditional expressions

"If" expressions

"If" syntax

The syntax for an if expression is simple: just put the true and false branches in indented blocks. main void() if true info log "True branch" else info log "False branch"

Using "if" as an expression

Crow's syntax technically does not have any statements; only expressions. Any line of code not at the end of a block is a void expression.

When an if appears in a void context, the true and false branches must also be void expressions.
A call to log works because it is a function returning void.

Since if is an expression, it can have a non-void type like string. So the first example can be rewritten as:

main void() info log if true "True branch" else "False branch"

"Elif"

You can add additional conditions to the chain using elif.

main void() info log if false "First condition is true (second condition not checked)" elif true "First condition was false, second condition is true" else "Neither condition is true"

"If" without "else"

If no else branch is written, it will default to (). This can create an empty value of most types; for example, it can be an empty string.

main void() info log "for 2: '{2 f}'" info log "for 4: '{4 f}'" f string(a nat) if a < 3 "True branch"

"Unless" expressions

unless works the same as if except the condition is negated. This doesn't support an else branch, since the double negation would be confusing.

main void() unless true info log "This won't log, because true"

Ternary expressions

There is an alternative syntax for short if expressions.

main void() x string = true ? "crow" : "duck" info log "x is '{x}'" # The false branch is still optional y string = false ? "crow" info log "y is '{y}'"

"guard" expressions

A guard expression is just like an if, except the branch where the condition is true is written underneath it, and the else branch is (optionally) written to the right after a :.
Since a guard is asymmetric, it's best used for cases where the true branch is the important one and the false branch returns an empty or error value.

main void() info log "{1 .. 3 overlaps 2 .. 4}" info log "{1 .. 3 overlaps 0 .. 2}" info log "{1 .. 3 overlaps 3 .. 5}" overlaps bool(a nat range, b nat range) guard a.high > b.low : false guard b.low < a.high : false true

A guard is useful for returning early from a long function. In Java, the first line would be written as if (!(a.high > b.low)) return false;..

As with if, the "else" branch is optional and defaults to (). (() for bool is false).

main void() info log "{1 .. 3 overlaps 2 .. 4}" overlaps bool(a nat range, b nat range) guard a.high > b.low guard b.low < a.high true

Scope

Each indented block of code has its own scope. Variables declared within the block can't be used outside of it.

main void() result = if true x nat = 1 x info log "{result}" # This won't work: # info log "{x}"

"Do" expressions

do is an unconditional expression. It's useful for introducing a new scope.

main void() info log do x string = "world" "Hello, {x}!"