Functions and Control Flow
Combine computations and branch according to conditions.
4.1 Define functions
Define functions with fn. Give types to parameters and results; the last expression supplies the result.
fn double(x: i64): i64 {
x * 2
}
fn add(a: i64, b: i64): i64 {
a + b
}
let result := add(double(4), 3)
perform show("result", result)
// Compose double and add to compute eleven.
・ double ・ add result = 11
result = 11
Use return to exit early. In short functions, using the final expression as the result is often easier to read.
fn early(x: i64): i64 {
if x < 0 { return 0 }
x
}
perform show("early", early(-3))
// Return early for negative inputs.
・ early early = 0
early = 0
4.2 if is an expression
fn absolute(x: i64): i64 {
if x < 0 { 0 - x } else { x }
}
let n := absolute(-12)
perform show("absolute", n)
// Both branches return the absolute value.
・ absolute absolute = 12
absolute = 12
Both branches of if return a value, which an enclosing expression can use. This composes expressions rather than merely splitting execution into paths.
4.3 Functions are values too
Write a lambda (an anonymous function) as x => expression.
fn twice(f: (i64) -> i64, x: i64): i64 {
f(f(x))
}
let result := twice(x => x + 10, 1)
perform show("twice", result)
// Pass a function as data and apply it twice.
・ twice twice = 21
twice = 21
The function type (i64) -> i64 accepts an i64 and returns an i64. twice takes a function as an argument and calls it twice.
4.4 Loops and termination
var i: i64 := 0
var sum: i64 := 0
while i < 4 {
sum := sum + i
i := i + 1
}
perform show("sum", sum)
// Accumulate zero through three with a mutable loop.
sum = 6
sum = 6
while is associated with the effect Div, meaning that evaluation may not terminate. This particular loop finishes because i increases, but an arbitrary while loop may run forever. Tauto reflects that possibility in types and effects. Chapter 15 covers well-founded recursion when you need a termination proof.
Exercise 4.1: Write maximum(a: i64, b: i64) -> i64 using an if expression.