5. Functions
A function gives a name to a calculation. Parameters describe its input. The declared result type describes its output.
Define and call a function
function greet(name : string) string {
-> "Hello, " + name
}
function RENDER(request : dval) {
print(greet("Ada"), "\n")
}
Output
Hello, Ada
The -> expression supplies the function result. Use return when a function must finish early.
Parameters and defaults
Parameters have names and types:
function label(name : string, suffix : string = "!") string {
-> name + suffix
}
function RENDER(request : dval) {
print(label("Capy"), "\n")
}
label("Capy") uses the default suffix. Defaults are trailing literals. A direct call can omit them, but a function value keeps the full parameter list.
Capy evaluates arguments once from left to right.
Overloads
Several functions can have the same name when their parameter types differ:
function describe(value : s32) string { -> "integer" }
function describe(value : string) string { -> "string" }
function RENDER(request : dval) {
print(describe(42), " ", describe("Ada"), "\n")
}
The argument types select the overload. A result type never selects one.
Conversion parameters
A parameter declared with as permits one constructor conversion:
function show(value : as string) {
print(value, "\n")
}
function RENDER(request : dval) {
show(42)
}
The call behaves as if it passed string(42). Capy does not chain several conversions.
Function values
A function can be stored and called through a typed value:
function double(value : s32) s32 { -> value * 2 }
function RENDER(request : dval) {
var operation : function(value : s32) s32 = double
var answer := operation(21)
print(answer, "\n")
}
A lambda defines a small function at its point of use:
function RENDER(request : dval) {
var add_one := function(value : s32) s32 {
-> value + 1
}
print(add_one(41), "\n")
}
A lambda can read captured outer values. Scalar captures copy. Array, dval, and struct captures share identity inside the workspace. A capture binding cannot be reassigned. The closure can mutate shared aggregate content.
function RENDER(request : dval) {
var offset := 2
var values := [10]
var update := function() s64 {
values.push(20)
-> offset + length(values)
}
print(update(), " ", values[1], "\n")
}
Store a callback in a dynamic value
A dval can store a named function or a closure in the current workspace. Read it with its complete function type:
function add_one(value : s32) s32 { -> value + 1 }
function RENDER(request : dval) {
var step := s32(2)
var callbacks := {named: add_one, captured: function(value : s32) s32 { -> value + step }}
var named : function(value : s32) s32 = callbacks.named
var captured : function(value : s32) s32 = callbacks.captured
print(named(1), " ", captured(1), "\n")
}
Output
2 3
The function type must match exactly. A mismatch traps. A callable does not cross a public boundary. BRRB, JSON, YAML, and XML replace it with none. Maps keep keys. Lists keep indexes.
Generic and variadic functions
An any parameter creates a compile-time generic function. Use it when the same operation is valid for several concrete types.
A variadic parameter collects values into a typed array:
function print_all(...values : as string) {
for value := values {
print(value)
}
}
function RENDER(request : dval) {
print_all("one", " ", "two", "\n")
}
Use ...items to spread an array into a variadic call.