Functions and Procedures
Part of: Reef Language Reference Last reviewed on version: 0.8.0
Functions vs Procedures
Functions (fn) - Return a value:
fn add(a: int, b: int): int
return a + b
end add
Procedures (proc) - No return value:
proc greet(name: string)
println(name)
end greet
Function Syntax
fn name(param1: type1, param2: type2): return_type
statements
return value
end name
Example:
fn multiply(x: int, y: int): int
return x * y
end multiply
proc main()
let result = multiply(6, 7)
print_int(result) // 42
println("")
end main
Procedure Syntax
proc name(param1: type1, param2: type2)
statements
end name
Example:
proc print_sum(a: int, b: int)
let sum = a + b
print_int(sum)
println("")
end print_sum
proc main()
print_sum(5, 3) // 8
end main
Parameters
Type annotations required:
fn calculate(x: int, y: int, z: int): int
return x + y * z
end calculate
All parameters are immutable (passed by value).
Argument evaluation order
Reef does not promise a single argument evaluation order for every call. The rule is per call site:
- At call sites the compiler instruments for GC argument rooting (any site where a GC-managed argument or receiver is evaluated before a later argument that may allocate — the common case for calls that build their arguments inline), arguments are evaluated strictly left to right, receiver first, then the listed arguments, then any defaulted parameters being filled in.
- At all other call sites the evaluation order is unspecified (it follows the underlying C compiler's argument order).
Do not write call sites whose correctness depends on the evaluation
order of side-effecting arguments: whether a given site is instrumented
is a compiler implementation detail and can change between releases.
The safe form is to bind side-effecting expressions with let first
and pass the names.
Default Parameters
Parameters can have default values that are used when the argument is omitted at the call site.
Syntax
fn name(required: type, optional: type = default_value): return_type
// ...
end name
Rules
-
Default parameters must come after required parameters - You cannot have a required parameter after a default parameter.
-
Default values must be compile-time expressions - Literals, constants, and simple expressions are supported.
-
Type must match - The default value must be compatible with the parameter type.
Examples
Single default parameter:
fn greet(name: string, greeting: string = "Hello"): string
return "${greeting}, ${name}!"
end greet
proc main()
println(greet("World")) // "Hello, World!"
println(greet("World", "Hi")) // "Hi, World!"
end main
Multiple default parameters:
fn format_number(n: int, prefix: string = "", suffix: string = ""): string
return "${prefix}${n}${suffix}"
end format_number
proc main()
println(format_number(42)) // "42"
println(format_number(42, "$")) // "$42"
println(format_number(42, "$", ".00")) // "$42.00"
end main
All parameters with defaults:
fn make_greeting(text: string = "Hello", times: int = 1): string
mut result: string = ""
mut i: int = 0
while i < times
result = "${result}${text}\n"
i = i + 1
end while
return result
end make_greeting
proc main()
println(make_greeting()) // "Hello\n"
println(make_greeting("Hi")) // "Hi\n"
println(make_greeting("Yo", 3)) // "Yo\nYo\nYo\n"
end main
Procedures with defaults:
proc log_message(msg: string, level: string = "INFO")
println("[${level}] ${msg}")
end log_message
proc main()
log_message("Starting...") // "[INFO] Starting..."
log_message("Oops!", "ERROR") // "[ERROR] Oops!"
end main
Default Parameters with Generic Functions
Default parameters work with generic functions, both with explicit type arguments and type inference:
fn wrap[T](value: T, label: string = "value"): string
return label
end wrap
proc main()
// With explicit type argument
println(wrap[:int](42)) // "value" (uses default)
println(wrap[:int](42, "number")) // "number"
// With type inference (no [:T] needed)
let x: int = 42
println(wrap(x)) // "value" (infers T=int)
println(wrap(x, "num")) // "num"
end main
See 055_GENERICS.md for more on generic functions.
Return Statements
Functions must return a value of the declared type:
fn get_value(): int
return 42
end get_value
Procedures can use return for early exit:
proc process(value: int)
if value < 0
return // Early exit, no value
end if
println("Processing...")
end process
Labeled Ends
fn calculate(x: int): int
return x * 2
end calculate
Both end calculate and end fn are valid.
The Main Function
Every Reef program requires a main entry point. You can use either form:
Procedure (no exit code):
proc main()
println("Hello, world!")
end main
Programs using proc main() always exit with code 0.
Function (with exit code):
fn main(): int
if some_error
return 1 // Exit with error
end if
return 0 // Exit with success
end main
Programs using fn main(): int return the specified exit code to the OS.
Immediate Exit:
Use exit(code) to terminate immediately from anywhere:
fn validate(): bool
if critical_error
exit(1) // Terminate program immediately
end if
return true
end validate
Next: 025_STRING_INTERPOLATION.md Previous: 015_BASICS.md