OxigenLang Functions
Functions in OxigenLang are first-class values — they can be assigned to variables, passed as arguments, and returned from other functions.
#Defining Functions
#Named Functions
Use the fun keyword followed by a name, parameters, and a body:
fun add(a, b) {
a + b
}
println(add(3, 4))#Anonymous Functions
Functions can be created without a name and assigned to variables:
double := fun(x) { x * 2 }
println(double(5))Anonymous functions are expressions, so they can appear anywhere an expression is expected.
#Parameters
#Untyped Parameters
By default, parameters accept any type:
fun greet(name) {
println("Hello, " + name + "!")
}
greet("Alice")#Typed Parameters
Add type annotations to enforce parameter types. The annotation uses the same <type> syntax as variable declarations:
fun add(a <int>, b <int>) {
a + b
}
add(3, 4)If a typed parameter receives a value of the wrong type, an error is produced:
fun add(a <int>, b <int>) { a + b }
add(3, "four")You can mix typed and untyped parameters:
fun format(label, value <int>) {
label + ": " + str(value)
}#Default Values
Parameters can have default values using = value after the type annotation. If the caller omits the argument, the default is used:
fun greet(name <str>, greeting <str> = "Hello") {
println("{greeting}, {name}!")
}
greet("Alice") // Hello, Alice!
greet("Bob", "Hey") // Hey, Bob!Default values work with untyped parameters too:
fun add(a, b = 0) { a + b }
println(add(5)) // 5
println(add(5, 3)) // 8#Optional Parameters
Mark a parameter as optional with ? after the name. Optional parameters default to None when omitted and skip type checking for None:
fun connect(host <str>, port? <int>) {
option {
port == None -> println("Connecting to {host} on default port"),
println("Connecting to {host}:{port}")
}
}
connect("localhost") // port is None
connect("localhost", 8080) // port is 8080#Mixing Required, Default, and Optional
All three forms can be combined. Required parameters must come first — the parser enforces this:
fun log(msg <str>, level <str> = "INFO", tag? <str>) {
option {
tag == None -> println("[{level}] {msg}"),
println("[{level}] [{tag}] {msg}")
}
}
log("server started") // [INFO] server started
log("request received", "DEBUG") // [DEBUG] request received
log("auth failed", "ERROR", "security") // [ERROR] [security] auth failedPlacing a required parameter after an optional or default parameter is an error:
fun bad(x <str> = "hi", y <int>) { ... }
// error: required parameter 'y' cannot follow optional/default parameters#Named Arguments
When calling a function, arguments can be passed by name using name=value syntax. This is especially useful with default and optional parameters, since it lets you skip parameters you don't need:
fun connect(host <str>, port <int> = 8080, tls? <bool>) {
println("host={host} port={port} tls={tls}")
}
connect("example.com") // positional only
connect("example.com", tls=True) // skip port, set tls
connect("example.com", port=443, tls=True) // named for bothNamed arguments can be in any order:
fun point(x <int>, y <int>) {
println("({x}, {y})")
}
point(y=20, x=10) // (10, 20)You can mix positional and named arguments — positional arguments must come first:
fun greet(name <str>, greeting <str> = "Hello") {
println("{greeting}, {name}!")
}
greet("Alice", greeting="Hey") // Hey, Alice!A positional argument after a named argument is an error:
greet(name="Alice", "Hey")
// error: positional argument cannot follow named argumentsNamed arguments are validated against the function's parameter names:
greet(unknown="Bob")
// error: unknown parameter name: 'unknown'#Return Values
Oxigen has no return keyword. A function returns the value of its last
expression, and give returns early. Writing return x is a syntax error
that points here.
#Implicit Return
The last expression in a function body is automatically returned:
fun square(x) { x * x }
println(square(5))For multi-statement bodies, the last expression is the return value:
fun process(x) {
doubled := x * 2
tripled := x * 3
doubled + tripled
}
println(process(4))#Explicit Return with give
Use give to return a value early:
fun abs(x) {
give -x when x < 0
x
}give immediately exits the function with the provided value. It works well with postfix guards for concise early returns:
fun safe_divide(a, b) {
give None when b == 0
a / b
}#Functions as Values
Functions are first-class — they can be stored in variables, passed to other functions, and returned:
#Storing in Variables
add := fun(a, b) { a + b }
sub := fun(a, b) { a - b }
println(add(5, 3))
println(sub(5, 3))#Passing as Arguments
fun apply(f, x) {
f(x)
}
double := fun(x) { x * 2 }
println(apply(double, 5))#Returning Functions
fun make_adder(n) {
fun(x) { x + n }
}
add5 := make_adder(5)
println(add5(10))#Closures
Functions capture variables from their enclosing scope. The captured variables remain accessible even after the outer function returns:
fun counter() {
count := 0
fun() {
count++
count
}
}
c := counter()
println(c())
println(c())
println(c())#Scope
Each function call creates a new environment (scope). Variables defined inside a function are local to that call:
x := "outer"
fun test() {
x := "inner"
println(x)
}
test()
println(x)Functions can read variables from outer scopes but create new bindings with :=:
multiplier := 10
fun scale(x) {
x * multiplier
}
println(scale(5))#Struct Methods
Functions can be attached to structs using includes blocks. Inside a method, struct fields are accessible directly by name (implicit self):
struct Circle {
radius <float>
}
Circle includes {
fun area() { 3.14159 * radius * radius }
fun scale(factor <float>) { radius = radius * factor }
}
c := Circle(5.0)
println(c.area())
c.scale(2.0)
println(c.area())See the Structs guide for full details on methods, implicit self, and inheritance.
#Recursion
Functions can call themselves:
fun factorial(n) {
give 1 when n <= 1
n * factorial(n - 1)
}
println(factorial(5))#Summary
| Feature | Syntax |
|---|---|
| Named function | fun name(params) { body } |
| Anonymous function | fun(params) { body } |
| Typed parameter | fun f(x <int>) { ... } |
| Default value | fun f(x <int> = 10) { ... } |
| Optional parameter | fun f(x? <int>) { ... } |
| Named argument | f(name="Alice", age=30) |
| Implicit return | Last expression in body |
| Explicit return | give value |
| Early return | give value when condition |
| Closure | Functions capture enclosing variables |
See also:
- Variables and Assignments — how function variables work
- Structs — attaching functions as methods
- Control Flow —
givewith postfix guards