Lambda

From GECK
Jump to: navigation, search

Lambdas are a new scripting feature added by xNVSE 6.1.0.


Also known as anonymous user defined functions, lambdas are nameless user defined functions which are placed inline within a parent script. Lambdas are a part of the NVSE Expression compiler and runtime.

Lambdas are simply UDFs form which are dynamically created inside a script with a particular syntax. As such, they can be captured in ref variables, or used directly in functions that accept script forms, like Event Handlers.

With the new script compiler, ref variables that are immediately assigned lambdas cannot be later reassigned. This is due to some optimizations that the compiler makes, and it also helps to prevent bugs that might occur from doing so.

A lambda placed in a variable

-- Obscript
ref rCallback
int iCount
let rCallback := (begin function {}
   let iCount += 1
   print "I have been called " + $iCount + " times."
end)

SetGameMainLoopCallback rCallback 1 1

-- NVSEScript
int iCount = 0;
ref rCallback = fn () {
    iCount++;
    print("I have been called ${iCount} times.");
}

SetGameMainLoopCallback(rCallback, 1, 1);

A lambda placed in an inline expression

Lambdas can be passed to any function that accepts a script as argument. They can access and modify all variables defined in the parent script.

-- Obscript
SetGameMainLoopCallback (begin function {}
   print "I have been called from an inline expression"
end) 1 1

-- NVSEScript
SetGameMainLoopCallback(fn () {
   print("I have been called from an inline expression");
}, 1, 1);

Single line lambda

If your lambda is only a single line long, and you optionally wish for the lambda to return the result of that line via implicitly calling SetFunctionValue, this syntax can be used:

UDF that takes a single parameter, and increments variable iCount by it

-- Obscript
let rCallback := {iSomeParam} => iCount += iSomeParam
Call rCallback 10 ; increment iCount by 10
Call rCallback 5  ; increment iCount by 5

-- NVSEScript
ref rCallback = fn (int iSomeParam) -> iCount += iSomeParam;
rCallback(10);
rCallback(5);

Makes every actor scream when hit

-- Obscript
SetOnHitEventHandler ({} => this.Say Hit) 1

-- NVSEScript
SetOnHitEventHandler(fn () -> Say(Hit), 1);

Prints the value of iCount each frame until iCount reaches 1000

-- Obscript
CallWhile ({} => print "iCount is " + $iCount) ({} => (iCount += 1) < 1000)

-- NVSEScript
CallWhile(fn () -> print("iCount is ${iCount}"), fn () -> iCount++ < 1000);

Lambda as an event handler

Lambdas are supported by event handlers as well. Usually you would define variables passed by an event inside its handler UDF, for example:

-- Obscript
scn OnKeyDownUDF

int iKeyID

begin Function {iKeyID}
    Print $iKeyID
end

SetOnKeyDownEventHandler OnKeyDownUDF 1 1

-- NVSEScript
name OnKeyDownUDF;

fn (int iKeyID) {
    Print($iKeyID);
}

SetOnKeyDownEventHandler(OnKeyDownUDF, 1, 1);

Using lambdas instead, you need to define these variables in the parent script that registers the handler, reducing the code above to just two lines. For example:

-- Obscript
int iKeyID

SetOnKeyDownEventHandler ({iKeyID} => Print $iKeyID) 1 1

-- NVSEScript
SetOnKeyDownEventHandler(fn (int iKeyID) -> Print($iKeyId), 1, 1);

Lambdas can also be used with the SetEventHandler-style event handlers:

-- Obscript
SetEventHandler "OnHit" (begin function {rTarget, rAttacker}
   ;Hit!
end) "first"::rTarget "second"::PlayerREF

-- NVSEScript
SetEventHandler("OnHit", fn (ref rTarget, ref rAttacker) {
   // Hit!
}, "first"::rTarget, "second"::PlayerREF);

Notes

  • You will need to update Hot Reload to the latest version so that lambdas can be hot reloaded. On hot reload, any lambdas within the reloaded script will get invalidated so you need to reregister any event handlers which utilize lambdas, best way is by adding GetGameHotReloaded after your GetGameLoaded if-statement that registers your event handlers.
  • A single line of GECK script is limited to 512 characters. This is not specific to Lambdas and Inline Expressions, but is more likely to be encountered due to their nature.
    • Calling a full UDF can be used to get around this limit. If you wish to have a script that doesn't rely on an ESP/ESM-added UDF, you may instead use CompileScript.
    • This limitation has been removed with the new script compiler

See Also