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: 10

Capture 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: 50

Owned 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 lambda

Reference 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 value

Multiple 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: 22

Void 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);
    }
]