Functions
Functions in Aria are pure by default, support full type inference, and use the last expression as the return value.
Basic Functions
// Multi-line function
fn add(a: i64, b: i64) -> i64 {
a + b
}
// Single-expression (no braces, no return)
fn double(x: f64) -> f64 = x * 2.0
// No return value (returns unit)
fn log(msg: str) {
println("[LOG] {msg}")
} The last expression in a block is the return value — no return keyword needed.
Parameters
All parameters are immutable by default. Use mut for mutable parameters:
fn push(mut stack: Stack[i64], value: i64) {
stack.items = stack.items.append(value)
} Generic Functions
Type parameters use square brackets [T] — no angle bracket ambiguity:
fn first[T](items: [T]) -> T? {
if items.len() == 0 { None } else { Some(items[0]) }
}
fn map[T, U](list: [T], f: fn(T) -> U) -> [U] =
[f(x) for x in list] Trait Bounds
Constrain type parameters with trait bounds:
fn largest[T: Ord](items: [T]) -> T? {
...
}
// Multiple bounds
fn sum[T: Ord + Numeric](values: [T]) -> T {
mut total: T = 0
for v in values { total = total + v }
total
}
// Where clause for complex bounds
fn process[T] where T: Display + Clone {
...
} Functions with Effects
Functions that perform side effects must declare them:
// Pure function (no effects) — safe to cache, parallelize, reorder
fn calculateTotal(items: [Item]) -> f64 =
items.map(.price).sum()
// Function with I/O and filesystem effects
fn readConfig(path: str) -> Config ! IoError with [Io, Fs] {
content := io.readFile(path)?
json.decode[Config](content)?
}
// Function with network effects
fn fetchData(url: str) -> [byte] ! HttpError with [Net] {
net.get(url)?.body
} Error-Returning Functions
Functions declare their error types in the signature with !:
// Returns str on success, IoError on failure
fn readFile(path: str) -> str ! IoError {
...
}
// Multiple error types via sum type
type AppError =
| Io(str)
| Parse(str)
| NotFound(str)
fn loadConfig(path: str) -> Config ! AppError {
content := io.readFile(path) catch |e| {
return err(AppError.Io("{e}"))
}
json.decode[Config](content) catch |e| {
return err(AppError.Parse("{e}"))
}
} Closures
// Inline closure
nums.map(fn(x) => x * 2)
// Multi-line closure
nums.filter(fn(item) {
item.isActive() && item.score > threshold
})
// Closures capture variables from their environment
multiplier := 3
scaled := nums.map(fn(x) => x * multiplier) Visibility
Functions are visible within their module by default. Use pub to export for other modules. The compiler enforces this with E0704 errors for private symbol access across files.