Arity-Based Dispatch
Ore supports multiple functions with the same name but different parameter counts (arities). The compiler resolves which function to call based on the number of arguments at the call site.
Basic Arity Dispatch
fn add(a: i32, b: i32) -> i32
a + b
fn add(a: i32, b: i32, c: i32) -> i32
a + b + c
fn main() -> !i32
add(1, 2) // calls add/2 → 3
add(1, 2, 3) // calls add/3 → 6
add(1, 2) + add(1, 2, 3) // 9
Each arity variant is a completely separate function with its own DefId, type signature, and compiled C name. add/2 compiles to ore_add_2, add/3 compiles to ore_add_3.
Rules
- Same name + same arity = compile error (duplicate definition)
- Same name + different arity = allowed (arity dispatch)
- Non-function definitions (structs, consts, variants) cannot share names
Higher-Order Functions
When passing a function as a value, the compiler infers which arity variant to use from the expected type:
fn apply(f: fn(i32, i32) -> i32, x: i32, y: i32) -> i32
f(x, y)
fn add(a: i32, b: i32) -> i32 { a + b }
fn add(a: i32, b: i32, c: i32) -> i32 { a + b + c }
fn main() -> !i32
apply(add, 20, 22) // apply expects fn(i32, i32) -> i32
// only add/2 matches → resolved
If there is only one function with a given name, no disambiguation is needed regardless of context:
fn double(x: i32) -> i32 { x * 2 }
items.map(double) // only one `double` → no ambiguity
If the compiler cannot determine which arity to use (rare — requires a fully generic parameter with no type constraints), it emits an error:
error: ambiguous function reference: multiple arities available, use in a call to disambiguate
Config Data (Alternative to Optional Parameters)
Ore uses fixed arity. When a function needs several related options, group them into a single data value instead of adding optional parameters.
ServerConfig :: data .{ .port: i32, .host: []const u8, .debug: i32 }
fn start_server(config: ServerConfig) -> i32
config.port
Aggregate Arguments
For nominal config data, construct the argument explicitly with a headed aggregate:
start_server(ServerConfig.{ .port = 3000, .host = "localhost", .debug = 0 })
If the parameter itself is structural, the expected type can drive an unheaded aggregate:
fn start_server(config: .{ .port: i32, .debug: i32 }) -> i32
config.port
start_server(.{ .port = 9000, .debug = 1 })
Name Mangling
All non-extern functions include arity in their C name: ore_{name}_{arity}. This ensures distinct C symbols even for same-name functions:
| Ore | C |
|---|---|
fn add(a, b) | ore_add_2 |
fn add(a, b, c) | ore_add_3 |
fn main() | ore_main_0 |
extern fn malloc(n) | malloc (unchanged) |
Extern functions keep their original C names for FFI compatibility.