Local variables

Using local variables

To define a local variable, write the name, then the type, then =, then the value.

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

Omitting the type annotation

The type can be omitted if the value has only one possible type. (String literals can take on a few types. See Basic types.)

main void() x = hello-world info log x hello-world string() "Hello, world!"

Mutable locals

By default, locals are read-only. Use the mut keyword to make it mutable. Then use := to change the value.
The type annotation remains optional. If present, it should go after mut.

main void() x mut string = "Hello, world!" info log x x := "goodbye" info log x

Short syntax for updates

Any function name (including operators) can be placed in front of the :=.
x foo:= y is shorthand for x := x foo y.

main void() x mut nat = 0 x +:= 1 info log "{x}"

Destructuring

When a value is a tuple, you can declare a variable for each component of the tuple instead of for the whole.

main void() x (string, string) = "hello", "world" greeting, target = x info log "Tell {target} I said {greeting}"

Ignored values

_ acts like a local name, but you can't use it to get the value of the local, marking it as intentionally unused.
Crow requires an expression in statement position to be void, so _ = is useful to explicitly drop a value.
It's also useful in destructuring if some of the members of a tuple are unused.

main void() # Just calling this for the side effect _ = func _, y = get-pair info log "{y}" func string() info log "side effect" "return value" get-pair (nat, nat)() 1, 2

The syntax for a local declaration applies recursively, so each component of a destructuring could be mutable, have a type annotation, be ignored, or even be another destructuring.

A parameters can also use destructuring, though it can't be mut.

main void() x (string, string) = "hello", "world" info log x.greet greet string((greeting string, target string)) "Tell {target} I said {greeting}"