Functions
Functions
A function is declared with fn, a parameter list, =>, and a return type.
fn join_path(base: string, name: string) => string {
return `${base}/${name}`
}A function that returns nothing omits the arrow and the type:
fn log_line(text: string) {
io::println(text)
}Default arguments
A parameter can carry a default, which makes it optional at the call site:
fn connect(host: string, port: number = 8080, secure: bool = false) => bool {
// ...
}
connect("localhost")
connect("localhost", 9000)
connect("localhost", 9000, true)Arguments are positional, so a call fills parameters left to right and may stop once the rest have defaults.
Returning
return exits with a value. Every path through a function that declares a return
type has to return, and the analyzer reports the ones that do not.
fn first_segment(path: string) => string? {
let cut = path.index_of("/")
if cut < 0 {
return None
}
return path.substring(0, cut)
}Generic functions
A function can take its own type parameters, with bounds written using impl:
[public] fn serialize<T: impl Serialize>(value: T) => JsonValue {
// ...
}Methods are generic the same way, independently of any parameters their class
declares. Array<T> is generic over its element type, and map adds a second
parameter of its own:
[public] fn map<U>(transform: closure(T) => U) => Array<U> {
// ...
}Here T comes from the class and U belongs to the method alone.
Functions as values
Functions are first-class values, so a named function can be stored, passed on, and returned.
A function type is written as the return type in parentheses, followed by the argument types in parentheses:
(ReturnType)(ArgType, ArgType)
So a function taking a number and returning a number has the type
(number)(number):
fn double(value: number) => number {
return value * 2
}
let transform: (number)(number) = double
let applied = transform(21)A function with no arguments is (number)(), and one returning nothing is
(void)(number).
This is distinct from a closure type, which is written closure(args) => return
and is the type of a closure literal. The two describe the same shape of call but
are not the same type, so a parameter written one way expects that form.
Pass a named function wherever its type is expected:
let doubled = numbers.map(double)Inferring type arguments
Type arguments are usually inferred rather than written. The analyzer infers them from the argument types and from the type the call is expected to produce:
let lengths = names.map(closure(name: string) => number {
return name.size()
})U is inferred as number from the closure's return type, so the call needs no
explicit <number>. Writing the argument out is still allowed when inference has
nothing to work from, or when being explicit reads better.