Skip to content

Functions & Signatures

Functions are the fundamental building blocks of execution in Wysna. They are declared with the fn keyword.

Every function parameter must include an explicit type annotation, and the return type must be designated with ->:

fn calculate_area(width: float, height: float) -> float {
return width * height
}
fn print_header(title: str) -> unit {
print("====================")
print(title)
print("====================")
}

If a function does not return any value, its return type must be declared as unit.


Functions can return multiple values bundled as lightweight product tuples (tuple[T1, T2, ...]):

fn divide_and_remainder(dividend: int, divisor: int) -> tuple[int, int] {
let quotient = dividend / divisor
let remainder = dividend % divisor
return (quotient, remainder)
}
fn main() -> unit {
let result = divide_and_remainder(17, 5)
match result {
(q, r) => {
print("Quotient:", q)
print("Remainder:", r)
}
}
}

In Wysna, parameter passing is governed by value semantics:

  1. Scalars (int, float, bool): Passed by value (copied efficiently into CPU registers).
  2. Managed Buffers (str, list, dict, structs): Passed as borrowed references under the hood by the compiler’s MIR tier. No memory copy occurs at function invocation time.
  3. If the callee modifies a received mutable parameter, the runtime’s Copy-on-Write (CoW) system creates a private copy solely for the callee, leaving the caller’s instance completely untouched.
fn append_element(items: list[int], val: int) -> list[int] {
let mut local_copy = items
local_copy.append(val)
return local_copy
}
fn main() -> unit {
let original = [1, 2, 3]
let modified = append_element(original, 4)
print("Original:", original) # Still [1, 2, 3]
print("Modified:", modified) # [1, 2, 3, 4]
}