Step 2. Functions
Why#
A function is the basic unit of reuse. In OxL the signature of a function is its contract: the parameter types, the result type and (see step 8) the effects are written in one place. You can therefore read code without looking into function bodies.
The body of a function is an expression: the last expression of a block is the result, return is only needed for an early exit. if and
match are expressions too, which is why the language has no ternary operator - it is not needed.
Example#
fn square(x: i32) -> i32 pure => x * x // pure means no side effects
fn greet(name: str, greeting: str = "Hello", loud: bool = false) -> str pure {
let text = "{greeting}, {name}"
if loud { text.upper() } else { text } // if is an expression; the last expression of a block is its value
}
// variadic parameter: inside the function xs is a slice
fn sumAll(...xs: i32) -> i32 pure {
var total = 0
for x in xs { total += x }
total
}
fn apply(f: fn(i32) -> i32, x: i32) -> i32 pure => f(x)
fn main() io {
print("{square(7)}")
print(greet("Ann"))
print(greet("Bob", loud: true)) // a named argument
print("{sumAll(1, 2, 3, 4)}")
let k = 10
let addK = (x: i32) => x + k // a closure captures k
print("{apply(addK, 5)} {apply(square, 6)}")
}Output
49 Hello, Ann HELLO, BOB 10 15 36
Walkthrough#
fn square(x: i32) -> i32 pure => x * x- the short form=> expression. The wordpuremeans "no side effects": such a function may be called from anywhere.greeting: str = "Hello"- a default value is evaluated on every call where the argument is omitted. Optional parameters are convenient to pass by name:greet("Bob", loud: true). Positional arguments come first, named ones after....xs: i32- a variadic parameter; inside the function it is a slice[i32]that you can walk withfor.(x: i32) => x + k- a lambda. It captureskfrom the surrounding code. A function can be passed as a value: the parameterf: fn(i32) -> i32accepts both a lambda and the ordinary functionsquare.- Signatures of
pub/pub(api)functions are written in full; for private functions the result type may be inferred from=> expression.
Common mistake: wrong number of arguments#
fn add(a: i32, b: i32) -> i32 => a + b
fn main() {
let s = add(1)
}error
arg-count
The compiler names the function and tells how many arguments were expected: arg-count. If a parameter
should be optional, give it a default value. A name of a nonexistent named parameter gives
named-arg.
Remember#
- The result is the last expression;
returnis for early exit. - Default values plus named arguments replace function overloading.
- Lambdas and functions are interchangeable wherever
fn(…) -> …is expected.
Next: data: data and enum.