Functions
Function Definition
Section titled “Function Definition”Functions are defined using the fn keyword:
fn greet(name: Str) Str { "Hello, {name}!"}Parameters and Return Types
Section titled “Parameters and Return Types”Function parameters require type annotations. Return types are specified after the parameter list. Without an explicit return type, Ard will treat the function as non-returning.
use go:fmt
fn add(a: Int, b: Int) Int { a + b}
// No return type specified - this function will not return a value.// Equivalent to declaring `Void` as the return typefn print_message(msg: Str) { fmt::Println(msg)}Reference parameters
Section titled “Reference parameters”A mut T parameter receives an actual mutable-reference value. Callers must pass an existing reference or create one explicitly with mut expression; declaring an ordinary binding with mut is not enough.
struct Person { name: Str, age: Int }
fn grow_older(person: mut Person) { person.age =+ 1}
let alice = Person{name: "Alice", age: 30}grow_older(mut alice)
let alice_reference = mut alicegrow_older(alice_reference)Reference parameters may mutate fields and call mutating methods, but Ard source does not support replacing a whole referent through the parameter. A function that needs an ordinary value must request T; callers with mut T use deref explicitly.
fn snapshot(person: mut Person) Person { deref person}Return Values
Section titled “Return Values”There is no return keyword in Ard. The last expression in a function is automatically returned:
fn multiply(x: Int, y: Int) Int { x * y}
fn get_status(code: Int) Str { match code { 200 => "OK" 404 => "Not Found" 500 => "Server Error" _ => "Unknown" }}Nullable Parameters
Section titled “Nullable Parameters”Function parameters can be marked as nullable using the ? modifier, allowing callers to omit them:
fn greet(name: Str, greeting: Str?) Str { let msg = greeting.or("Hello") "{msg}, {name}!"}
// Providing a value for the nullable parametergreet("Alice", "Hi")
// Omitting the nullable parameter (greeting becomes None)greet("Bob")When a non-nullable value is provided to a nullable parameter, it’s automatically wrapped in Maybe::new():
struct Options { verbose: Bool,}
fn process(data: Str, options: Options?) { let opts = options.or(Options{verbose: false}) // Process with options}
// Automatically wraps the provided value in Maybeprocess("data", Options{verbose: true})
// Omits the parameter (becomes none)process("data")Omitting Nullable Parameters
Section titled “Omitting Nullable Parameters”You can omit any trailing nullable parameters in a function call. They will be treated as None:
fn configure(name: Str, timeout: Int?, retries: Int?, debug: Bool?) { // All nullable parameters are optional}
// You can provide all, some, or none of the nullable parametersconfigure("service", 30, 3, true) // All providedconfigure("service", 30, 3) // debug omittedconfigure("service", 30) // retries and debug omittedconfigure("service") // All nullable params omittedLabelled Arguments
Section titled “Labelled Arguments”Functions can be called with labelled arguments, allowing parameters to be specified in any order:
struct User { name: Str, age: Int, email: Str,}
fn create_user(name: Str, age: Int, email: Str) User { User{name: name, age: age, email: email}}
// Positional arguments (order matters)create_user("Alice", 25, "alice@example.com")
// Named arguments (order doesn't matter)create_user(age: 30, email: "bob@example.com", name: "Bob")Positional and named arguments can be mixed, but positional arguments must come first:
// Allowed: positional, then namedcreate_user("Charlie", age: 35, email: "charlie@example.com")
// NOT allowed: positional after namedcreate_user(name: "Charlie", 35, "charlie@example.com")First-Class Functions
Section titled “First-Class Functions”Functions are first-class values and can be used as arguments:
fn apply(value: Int, transform: fn(Int) Int) Int { transform(value)}
fn double(x: Int) Int { x * 2}
// Pass a named function as an argumentlet doubled = apply(4, double)Anonymous Functions
Section titled “Anonymous Functions”Functions can be defined inline without names:
fn apply(value: Int, transform: fn(Int) Int) Int { transform(value)}
let squared = apply(3, fn(x: Int) Int { x * x })Function Signatures
Section titled “Function Signatures”When referring to function types, use the fn syntax and just omit the body:
use go:fmt
fn add(a: Int, b: Int) Int { a + b }fn shout(msg: Str) { fmt::Println(msg) }fn get_random_number() Int { 4 }
let operation: fn(Int, Int) Int = addlet printer: fn(Str) = shoutlet generator: fn() Int = get_random_numberUse ? after the function type for nullable function values. If the function type has an explicit return type, wrap the whole type in parentheses so the ? applies to the function instead of the return type:
fn lookup_name(id: Int) Str? { Maybe::new<Str>()}
let optional_printer: fn(Str)? = Maybe::new() // nullable fn(Str) Voidlet optional_mapper: (fn(Int) Str)? = Maybe::new() // nullable fn(Int) Strlet maybe_name: fn(Int) Str? = lookup_name // non-null function returning Str?fn(Int) Void? is rejected because it is ambiguous and usually means an optional callback. Use fn(Int)? or (fn(Int) Void)? instead.