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#
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:
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:
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]:
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:
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:
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:
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:
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.
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.

