Closures
Closures (anonymous functions) allow you to create blocks of code that can be passed around as variables and capture values from their surrounding scope. In Autolang, closure syntax is highly flexible but follows specific mandatory rules.
Basic Syntax
Every closure in Autolang must define its parameters inside vertical bars ||. You can define explicit data types for the parameters, or let the compiler infer them. If a closure requires a multi-line body, you can combine it with the arrow -> syntax.
// 1. Closure with no parameters (empty || is mandatory)
val sayHello = {|| println("Hello from Autolang!") }
sayHello()
// 2. Closure with parameters and an explicit type (Single expression)
val square = {|x: Int| x * x }
println(square(5)) // Prints: 25
// 3. Closure with a block body using ->
val checkEven: Int -> Void = {|num| -> {
if (num % 2 == 0) {
println("Even")
} else {
println("Odd")
}
}}
checkEven(10) // Prints: Even⚠️ Mandatory Syntax Rule
In Autolang, the || parameter delimiter is strictly required when declaring a closure. If you forget the || (for example, just writing literal body), the compiler will throw a syntax error.
Using Closures with Built-in Methods
Closures are extremely useful when working with data structures like Arrays. You can pass a closure directly into methods like forEach to iterate and manipulate elements.
val numbers = <Int>[1, 2, 3, 4, 5]
// Passing a closure directly as an argument to forEach
numbers.forEach {|value|
println("Processing: " + value)
}
// Or executing multiple lines of code inside a block body
var sum = 0
numbers.forEach {|value| -> {
sum = sum + value
println("Current sum: " + sum)
}}Trailing Closure Syntax
When a closure is the last argument of a function call, you can move it outside the parentheses. This is called trailing closure syntax and makes the code more readable — especially with methods like forEach, filter, and sort.
fun calculate(a: Int, b: Int, op: (Int, Int) -> Int): Int = op(a, b)
// Standard call: closure inside parentheses
val r1 = calculate(4, 5, {|a, b| a + b})
println(r1) // 9
// Trailing closure: moved outside parentheses
val r2 = calculate(4, 5) {|a, b| a * b}
println(r2) // 20Value Capturing
Closures capture variables from their enclosing scope by value at the time the closure is created, not by reference. This means changes to the original variable after closure creation do not affect the captured copy inside the closure.
var x = 500
// Closure captures the value of x (500) at creation time
val snapshot = {|| x}
// Changing x later does NOT affect the captured value
x = 999
println(snapshot()) // 500 <-- captured value, not the new x
println(x) // 999 <-- current value of x⚠️ Note on Mutable Captures
Because primitives are captured by value, a closure can only read the value at the time of creation. If you need shared mutable state, wrap the value in a class (e.g., a Box<T> wrapper).
