Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Functions

A func takes data and returns data. The parameter list is one group, in the order written, and a call supplies that whole group.

// Returning functions, a lambda, and a generic.

func double(n: i64) -> i64 {
    <(n, 2) | mul
}

func greet(name: String) -> String {
    <("Hello, ", name) | add
}

func id<+T>(x: T) -> T { x }

proc main | (exit: i32) / {IO} {
    <21 | double | println;
    <"SLC" | greet | println;
    let square = fn(n: i64) -> i64 { <(n, n) | mul };
    <6 | square | println;
    <7 | id | println;
    <"seven" | id | str_len | println;
    <0 | exit>
}
42
Hello, SLC
36
7
5

Declaration and call

func double(n: i64) -> i64 {
    <(n, 2) | mul
}

-> i64 is the result. <21 | double sends one integer in. A function of several parameters takes them as one tuple, in the declared order:

func greet(name: String) -> String {
    <("Hello, ", name) | add
}

<"SLC" | greet is the call. <("Hello, ", "SLC") | greet would be a type error, because greet takes one String. Partial application is a type error as well: the group arrives whole, or the call is refused.

The body is a block. Its value is the last expression. A semicolon separates statements before that expression.

A parenthesized call double(21) is refused for a named function. The diagnostic tells you to flow the argument in. Two spellings do use parentheses: a nullary call, f() or <(,) | f, and a constructor, Shape::Circle(5). The name f alone is the function, and naming it runs nothing.

Lambdas

fn is a function with no declaration name.

let square = fn(n: i64) -> i64 { <(n, n) | mul };
<6 | square | println;

A lambda parameter needs a polarity the checker can see. A type annotation supplies it. Immediate application of a lambda keeps the parentheses the declaration does not: <fn(x: i64) -> i64 { x }(42) | println.

Generics

A type parameter is declared in angles, with a sign. + means the parameter stands for a value type.

func id<+T>(x: T) -> T { x }

Each call chooses its own T. <7 | id is an integer, and <"seven" | id is a string. Inside the body, T is rigid: the function cannot inspect it except through a trait bound, which the traits chapter adds.

A let generalizes a value, such as a lambda or a variant that carries no payload. A computation stays monomorphic. The usual way to name a polymorphic function is a func declaration.

Effects on the arrow

A function that performs an effect says so after the result:

func greet(name: String) -> (,) / {IO} {
    <name | println
}

A bare -> is an empty effect row: the function performs nothing. The effects chapter is where a row is handled.

Continuations writes the other half of a cut: the continuation a value is sent to.