Functions
Functions are the core building block in Almide. Every function has explicit parameter types and a return type. The body is a single expression after =.
Basic syntax
Section titled “Basic syntax”fn add(a: Int, b: Int) -> Int = a + b
fn greet(name: String) -> String = "Hello, ${name}!"Key rules:
- Parameter types are required
- Return type is required
- The body follows
=(not{}) - The last expression is the return value (no
returnkeyword)
Multi-statement bodies
Section titled “Multi-statement bodies”Use a block { } for multiple statements. The last expression in the block is the return value:
fn classify(n: Int) -> String = { let abs_n = int.abs(n) let label = if abs_n > 100 then "large" else "small" "${label}: ${int.to_string(n)}"}effect fn
Section titled “effect fn”Functions with side effects (I/O, network, randomness) must be marked effect fn:
import fs
effect fn save(path: String, content: String) -> Result[Unit, String] = { fs.write(path, content)! ok(())}The effect modifier enforces a strict boundary:
- Calling an
effect fnfrom a non-effect function is a compile error effect fntypically returnsResult[T, E]- Use the
!operator to unwrapResultvalues and propagate errors explicitly
fn pure() -> String = fs.read_text("file.txt") // Compile error: cannot call effect fn
effect fn safe() -> Result[String, String] = fs.read_text("file.txt") // OK: effect fn calling effect fnSee Error Handling for the !, ??, and ? operators.
Parameters
Section titled “Parameters”Required parameters
Section titled “Required parameters”All parameters require type annotations:
fn distance(x1: Float, y1: Float, x2: Float, y2: Float) -> Float = float.sqrt((x2 - x1) ^ 2.0 + (y2 - y1) ^ 2.0)Default arguments
Section titled “Default arguments”Parameters can have default values. All parameters after the first default must also have defaults:
fn connect(host: String, port: Int = 8080, secure: Bool = false) -> String = "${host}:${int.to_string(port)}"Named arguments
Section titled “Named arguments”Arguments can be named at the call site, useful with default parameters:
connect("localhost")connect("localhost", port: 443, secure: true)connect(host: "localhost", secure: true) // skip port, use defaultNamed arguments after positional ones are allowed. Positional arguments after named ones are not:
connect("localhost", secure: true) // OKconnect(secure: true, "localhost") // Compile errorMutable parameters
Section titled “Mutable parameters”mut on a parameter passes it by mutable reference: an assignment inside the
function is visible to the caller after the call returns, in place — there’s
no return value carrying the new state back.
fn incr(mut x: Int) -> Unit = { x = x + 1 }
fn main() -> Unit = { var n = 5 incr(n) println(int.to_string(n)) // 6}The caller must pass a var binding — a let binding or a temporary
expression is rejected, since there’s nothing to write the mutation
back into. mut can appear on any parameter, not just the first. This is how
in-place stdlib operations are written (list.push, list.pop,
list.clear, …) — an ordinary parameter marked mut, not a hidden receiver
convention.
Predicate functions
Section titled “Predicate functions”Predicates are named with an is_ prefix, matching the standard library
(string.is_empty, list.is_empty, string.is_digit):
fn is_empty(xs: List[Int]) -> Bool = list.len(xs) == 0fn is_positive(n: Int) -> Bool = n > 0There is no ? suffix in identifiers — fn empty?(…) is a syntax error.
(? is an operator, used postfix on a Result to turn it into an Option.)
Lambdas
Section titled “Lambdas”Lambdas (anonymous functions) use the (params) => expr syntax:
let double = (x: Int) => x * 2Type annotations on lambda parameters are optional when inferrable:
let xs = [1, 2, 3]let doubled = list.map(xs, (x) => x * 2)let evens = list.filter(xs, (x) => x % 2 == 0)Multi-parameter lambdas:
let add = (a: Int, b: Int) => a + blist.fold([1, 2, 3], 0, (acc, x) => acc + x) // 6Use _ for unused lambda parameters:
list.map(xs, (_) => 0) // replace every element with 0There is only one lambda syntax. No shorthand forms like {x -> x} or x => x.
Visibility
Section titled “Visibility”Three visibility levels control access to functions:
| Modifier | Scope | Rust equivalent |
|---|---|---|
| (none) | Public (default) | pub |
mod | Same project only | pub(crate) |
local | Same file only | (private) |
fn public_api() -> String = "anyone can call this"mod fn internal_helper() -> String = "project-internal"local fn file_private() -> String = "only this file"Visibility applies to fn, type, and let declarations:
local type Internal = { data: String }mod let THRESHOLD = 100Modifier order
Section titled “Modifier order”When combining modifiers, the order is: [local|mod]? effect? fn
mod effect fn internal_io() -> Result[Unit, String] = { println("internal") ok(())}
local effect fn private_io() -> Result[Unit, String] = { println("private") ok(())}Holes and todo
Section titled “Holes and todo”Use _ (hole) or todo(msg) as placeholder implementations:
fn parse(text: String) -> Ast = _ // type-checked stubfn optimize(ast: Ast) -> Ast = todo("implement later") // todo with messageA hole type-checks against whatever type is expected; reaching one at runtime panics.
UFCS (Uniform Function Call Syntax)
Section titled “UFCS (Uniform Function Call Syntax)”f(x, y) and x.f(y) are equivalent. The compiler resolves automatically:
string.trim(text) // canonical module.function formtext.trim() // UFCS dot-call form (equivalent)
string.split(text, ",")text.split(",") // equivalentPipe operator
Section titled “Pipe operator”The pipe operator |> passes a value as the first argument to the next function:
text |> string.trim |> string.split(",")// equivalent to: string.split(string.trim(text), ",")The piped value always becomes the first argument; there is no placeholder for other positions. Call the function directly when it does not fit:
list.filter(xs, (x) => x > 0)Pipe into match:
list.get(args, 1) |> match { some(cmd) => cmd, none => "default",}Next steps
Section titled “Next steps”- Control Flow — if, for, while, guard
- Error Handling — Result, Option, unwrap operators, effect fn
- Modules & Imports — organizing code across files