Functions & Signatures
Functions are the fundamental building blocks of execution in Wysna. They are declared with the fn keyword.
Declaring Functions
Section titled “Declaring Functions”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.
Multiple Return Values & Tuples
Section titled “Multiple Return Values & Tuples”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) } }}Parameter Passing Semantics
Section titled “Parameter Passing Semantics”In Wysna, parameter passing is governed by value semantics:
- Scalars (
int,float,bool): Passed by value (copied efficiently into CPU registers). - 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. - 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]}