Lambdas
XXML supports anonymous functions (lambdas) with closures, capturing variables from the enclosing scope with explicit ownership semantics.
Lambda Syntax
[ Lambda [captures] Returns ReturnType Parameters (parameters) {
// body
}]Basic Lambda
// Lambda with no captures
Instantiate F(Integer^)(Integer&)^ As <double> = [ Lambda [] Returns Integer^ Parameters (
Parameter <n> Types Integer&
) {
Return n.multiply(Integer::Constructor(2));
}];
// Calling the lambda
Instantiate Integer^ As <result> = double.call(Integer::Constructor(5));
Run Console::printLine(result.toString()); // Prints: 10Capture Modes
Lambdas can capture variables from the enclosing scope with three different ownership semantics:
Copy Capture (%)
Creates an independent copy of the variable inside the lambda:
Instantiate Integer^ As <multiplier> = Integer::Constructor(5);
Instantiate F(Integer^)(Integer&)^ As <multiply> = [ Lambda [%multiplier] Returns Integer^ Parameters (
Parameter <n> Types Integer&
) {
Return n.multiply(multiplier);
}];
// multiplier is still accessible here
Instantiate Integer^ As <result> = multiply.call(Integer::Constructor(10));
Run Console::printLine(result.toString()); // Prints: 50Owned Capture (^)
Moves ownership of the variable into the lambda:
Instantiate String^ As <secret> = String::Constructor("password123");
Instantiate F(String^)()^ As <getSecret> = [ Lambda [^secret] Returns String^ Parameters () {
Return secret;
}];
// secret is NO LONGER accessible here - ownership moved to lambdaReference Capture (&)
Captures a reference to the variable:
Instantiate Integer^ As <counter> = Integer::Constructor(0);
Instantiate F(Integer^)()^ As <getCount> = [ Lambda [&counter] Returns Integer^ Parameters () {
Return counter;
}];
// counter is still owned here, lambda just borrows it
Set counter = counter.add(Integer::Constructor(1));
Instantiate Integer^ As <count> = getCount.call(); // Returns current counter valueMultiple Captures
Instantiate Integer^ As <a> = Integer::Constructor(5);
Instantiate Integer^ As <b> = Integer::Constructor(10);
Instantiate Integer^ As <ref> = Integer::Constructor(100);
Instantiate F(Integer^)()^ As <compute> = [ Lambda [%a, ^b, &ref] Returns Integer^ Parameters () {
Instantiate Integer^ As <sum> = a.add(b);
Return sum.add(ref);
}];
// a is still accessible (was copied)
// b is NOT accessible (was moved)
// ref is still accessible (was borrowed)Lambdas with Multiple Parameters
Instantiate F(Integer^)(Integer&, Integer&)^ As <add> = [ Lambda [] Returns Integer^ Parameters (
Parameter <x> Types Integer&,
Parameter <y> Types Integer&
) {
Return x.add(y);
}];
Instantiate Integer^ As <sum> = add.call(
Integer::Constructor(15),
Integer::Constructor(7)
);
Run Console::printLine(sum.toString()); // Prints: 22Void Lambdas
Instantiate F(None)(String&)^ As <printer> = [ Lambda [] Returns None Parameters (
Parameter <msg> Types String&
) {
Run Console::printLine(msg);
}];
Run printer.call(String::Constructor("Hello from lambda!"));Lambda Templates
Lambdas can have their own type parameters:
Instantiate __function^ As <identity> = [ Lambda [] Templates <T Constrains None> Returns T^ Parameters (
Parameter <x> Types T^
) {
Return x;
}];
// Call with different types
Instantiate Integer^ As <intResult> = identity<Integer>.call(Integer::Constructor(42));
Instantiate String^ As <strResult> = identity<String>.call(String::Constructor("Hello"));Lambda Templates with Constraints
[ Constraint <Printable> <T Constrains None> (T a)
Require (F(String^)(toString)(*) On a)
]
Instantiate __function^ As <printValue> = [ Lambda [] Templates <T Constrains Printable> Returns None Parameters (
Parameter <value> Types T^
) {
Run Console::printLine(value.toString());
}];
Run printValue<Integer>.call(Integer::Constructor(42));
Run printValue<String>.call(String::Constructor("Hello"));Function Types
The function type syntax is F(ReturnType)(ParamTypes...):
// Function taking no parameters, returning Integer
F(Integer^)()
// Function taking Integer&, returning String
F(String^)(Integer&)
// Function taking two parameters
F(Integer^)(Integer&, Integer&)
// Function returning nothing
F(None)(String&)Passing Lambdas to Methods
[ Class <Processor> Final Extends None
[ Public <>
Constructor = default;
Method <process> Returns Integer^ Parameters (
Parameter <value> Types Integer^,
Parameter <transform> Types F(Integer^)(Integer&)^
) Do
{
Return transform.call(value);
}
]
]
[ Entrypoint
{
Instantiate Processor^ As <proc> = Processor::Constructor();
Instantiate F(Integer^)(Integer&)^ As <triple> = [ Lambda [] Returns Integer^ Parameters (
Parameter <n> Types Integer&
) {
Return n.multiply(Integer::Constructor(3));
}];
Instantiate Integer^ As <result> = proc.process(
Integer::Constructor(10),
triple
);
Run Console::printLine(result.toString()); // Prints: 30
Exit(0);
}
]