Docs/Language basics

Functions

Define functions with defaults, named arguments and several results.

On this page

A function lists its parameter types and its result type. Locals inside the body still infer their types.

Define and call a function#

Valk
fn line_total(price: int, quantity: int) int {
    return price * quantity
}

fn log(message: String) {
    println("[log] " + message)
}

fn main() {
    println(line_total(12, 3)) // 36
    log("started")             // no result type: returns nothing
}

Functions can be declared in any order. Arguments are evaluated left to right.

Default values#

A default goes in parentheses after the parameter type:

Valk
fn greet(name: String ("world"), end: String ("!")) String {
    return "Hello " + name + end
}

fn main() {
    println(greet())            // Hello world!
    println(greet("Ada"))       // Hello Ada!
    println(greet(_, "?"))      // Hello world?  (_ keeps a default)
}

Named arguments#

After the positional arguments, arguments can be given by name, in any order. Parameters with a default can be left out:

Valk
fn resize(width: int, height: int, keep_ratio: bool (false)) String {
    return "%width x %height, ratio %keep_ratio"
}

fn main() {
    println(resize(height: 5, width: 7))         // 7 x 5, ratio false
    println(resize(800, 600, keep_ratio: true))  // 800 x 600, ratio true
}

Any number of arguments#

A last parameter written name: ...Type collects the remaining arguments into an Array[Type]:

Valk
fn sum(values: ...int) int {
    let total = 0
    each values as v : total += v
    return total
}

fn main() {
    println(sum())          // 0
    println(sum(1, 2, 3))   // 6
    let numbers = Array[int]{ 4, 5 }
    println(sum(...numbers)) // 9: pass an existing array
}

Return several values#

A function returns several values as a tuple:

Valk
fn size() (int, int) {
    return (640, 480)
}

fn main() {
    let width, height = size()
    println("%width x %height")   // 640 x 480
    let both = size()
    println(both[1])              // 480
    let a = 1
    let b = 2
    (a, b) = (b, a)               // swap
    (_, height) = size()          // _ skips a value
    println("%a %b %height")      // 2 1 480
}

Tuples compare with ==, and with < and > member by member.

Read command-line arguments#

Declare an Array[String] parameter on main. Index 0 is the program's path:

Valk
fn main(args: Array[String]) {
    let name = args.get(1) !? "Guest"
    println("Welcome, %name")
    each args skip 1 as arg : println("arg: " + arg)
}

Run it with valk run main.valk -- Ada or ./app Ada. For flags and subcommands, see the cli package in Ecosystem packages.

Run cleanup before returning#

defer runs a call when the function returns, also when it returns with an error:

Valk
fn work() {
    defer println("cleanup 1")
    defer println("cleanup 2")
    println("working")
}

fn main() {
    work() // working, cleanup 2, cleanup 1
}

Deferred calls run in reverse order. Their arguments are evaluated when the defer line is reached. Put the defer right after acquiring a resource:

Valk
use valk.fs

fn header_size(path: String) uint {
    let file = fs.stream(path) ! return 0
    defer file.close() _
    let buffer = [u8]{ 0 x 64 }
    return file.read(buffer) !? 0
}

fn main() {
    println(header_size("notes.txt"))
}

A deferred call that can fail handles its error on the same line; _ above ignores a close error. defer { ... } defers several statements at once.

Functions that can fail#

Add !ErrorType after the result type and use throw. See Error handling.

Valk
fn half(value: int) int !AnError {
    if value % 2 != 0 : throw .error
    return value / 2
}

fn main() {
    println(half(8) !? -1)  // 4
    println(half(7) !? -1)  // -1
}

To pass a function as a value, see Closures.