Skip to content

Lambdas

Ahmad Saleem edited this page Aug 25, 2026 · 9 revisions

Lambdas

A lambda is a small function that you can save in a variable, pass around, and run later.

Think of it like saving a recipe in a box. The recipe doesn't cook anything by itself. You give the box to someone, and they cook with it when they want to.

Making a lambda

set {_double} to lambda (n: number) -> number:
    return {_n} * 2

What this says:

  • lambda starts a lambda.
  • (n: number) means it takes one argument called n, which must be a number.
  • -> number means it gives back a number.
  • return {_n} * 2 is the value it gives back, n * 2.

Inside the body, the arguments become local variables: n becomes {_n}.

Lambdas with no return value

If the lambda just does something and gives nothing back, leave off the -> type.

set {_greet} to lambda (p: player):
    send "Hello %{_p}%!" to {_p}

Lambdas with no arguments

If the lambda takes no input, leave off the ( ... ).

set {_now} to lambda -> number:
    return unix timestamp of now

Inline lambdas

If the body is just one line, you can write the whole lambda on one line. Put the body right after the :.

set {_is-op} to lambda (p: player): {_p} is op

This is handy for short lambdas. The body can be one of three things:

  • A condition (like {_p} is op). The lambda gives back true or false. This is called a predicate. See Predicates.
  • An effect (like send "hi" to {_p}). The lambda just does it and gives nothing back.
  • A value. The lambda gives that value back. Write it with return, or just leave a bare expression and it's returned for you.
# a predicate: returns yes/no
add lambda (n: number): {_n} > 0 to {positive-checks::*}

# an effect: just does something
run lambda (p: player): send "hi" with player

# a value: both of these return a number
set {_add} to lambda (a: number, b: number): return {_a} + {_b}
set {_double} to lambda (n: number): {_n} * 2

Returning a value from a one-line lambda is new in 1.1.0. Before, a one-liner could only test something or do something. Now lambda (n): {_n} * 2 hands the value back, so inline lambdas drop straight into the list operations like mapped with.

For longer bodies, or when you want to be explicit, use the section form (the lambda ...: block shown above) instead.

Calling a lambda

There are two ways to use a lambda after you have one.

call: when you want the value back

set {_x} to call lambda {_double} with 5
# {_x} is now 10

There are a few ways to write this. They all mean the same thing:

set {_x} to call lambda {_double} with 5
set {_x} to invoke lambda {_double} with 5
set {_x} to calling lambda {_double} with 5
set {_x} to the result of calling lambda {_double} with 5

Pick whichever reads best in your line.

set {_t} to invoke lambda {_now}

If the lambda takes more than one argument, separate them with commas:

set {_sum} to lambda (a: number, b: number) -> number:
    return {_a} + {_b}

set {_total} to call lambda {_sum} with 3, 4
# {_total} is 7

run: when you don't care about the return value

run lambda {_greet} with player

run is for lambdas that just do something. It throws away any return value. Since 1.5.0 call lambda {_x} and invoke lambda {_x} also work on their own line, if that reads better to you.

Lambdas that return a list

A lambda body can return a list. call lambda gives you the first value, as it always has; the plural spelling gives you all of them.

set {_top3} to lambda -> object:
    set {_best::*} to "gold", "silver", "bronze"
    return {_best::*}

set {_first} to call lambda {_top3}                 # gold
set {_all::*} to results of calling lambda {_top3}  # gold, silver, bronze

Added in 1.5.0. Before, a list return failed with a confusing parse error and then handed back nothing.

Default parameter values

A parameter can carry a default with = value. When the caller leaves that argument off, the default fills in.

set {_advance} to lambda (value: number, step: number = 1) -> number:
    return {_value} + {_step}

set {_a} to call lambda {_advance} with 10        # step defaults to 1  ->  11
set {_b} to call lambda {_advance} with 10, 5     # 15

Only trailing arguments can be skipped. On a two-parameter lambda, call ... with 10 fills the second parameter from its default; you can't supply the second while skipping the first.

A parameter with no default is required. Since 1.5.0, calling a lambda without one gives you nothing back rather than running the body anyway, so handing a two-argument lambda to something that supplies one ((1, 2) mapped with {_two-arg}) reads as nothing instead of a plausible-looking wrong number.

A default can be any expression, brackets, commas and quotes included: = (1 + 2), = max(1, 9) and = "x, y" all work. Before 1.5.0 those broke the signature parser.

Added in 1.2.0.

Lambdas that return lambdas

A lambda can return another lambda. This lets you build small "factories".

set {_make_adder} to lambda (n: number) -> object:
    set {_inner} to lambda (x: number) -> number:
        return {_x} + {_n}
    return {_inner}

set {_plus5} to call lambda {_make_adder} with 5
set {_r} to call lambda {_plus5} with 10
# {_r} is 15

The inner lambda remembers {_n} from the outer one. So {_plus5} is now a lambda that always adds 5.

A lambda that calls itself

A lambda has no name to call itself by, so to recurse it takes itself as an argument and runs it. Pass the lambda as its own first argument, then call that parameter inside the body.

set {_fact} to lambda (self: object, n: number) -> number:
    if {_n} <= 1:
        return 1
    return {_n} * (call lambda {_self} with {_self}, {_n} - 1)

set {_r} to call lambda {_fact} with {_fact}, 5
# {_r} is 120

The self parameter is the lambda itself; calling {_self} and handing it forward (with {_self}, ...) each time keeps the chain going. This self-passing pattern is the intended way to recurse. For a full real-world build, see the recursive vein miner in Examples.

Capturing nearby variables

More generally, a lambda keeps a snapshot of the local variables ({_x}) around it from the moment you wrote it. When the lambda runs later, even from a completely different trigger, it can still read them. (This is new in 1.1.0, and it's also what makes the factory above work.)

set {_tax} to 0.2
set {_with-tax} to lambda (price: number) -> number:
    return {_price} * (1 + {_tax})       # {_tax} is captured here

set {_total} to call lambda {_with-tax} with 100     # 120

Three things to know:

  • It's a copy taken at definition time. Changing {_tax} afterwards won't change what the lambda sees, and anything the lambda does to {_tax} inside its body won't leak back out.
  • If a parameter shares a name with a captured local, the parameter wins inside the body.
  • This only concerns locals ({_x}). Global variables ({x}, {-x}) were always shared across everything, and still are.

Pre-filling arguments: bound

%lambda% with %values% bound makes a new lambda with the first argument (or arguments) already filled in. It's a head start: an "add two numbers" lambda becomes "add 5 to whatever you give me."

set {_add} to lambda (a: number, b: number) -> number:
    return {_a} + {_b}

set {_add5} to {_add} with 5 bound          # a one-argument lambda
set {_x} to call lambda {_add5} with 10     # 15
set {_y} to call lambda {_add5} with 100    # 105

Bind every argument and you get a zero-argument lambda (a "thunk") that just returns the answer when called:

set {_answer} to {_add} with (40, 2) bound
set {_r} to call lambda {_answer}           # 42

Added in 1.1.0. For flipping a predicate's result, see negated.

Chaining lambdas: pipe

pipe %value% through %lambdas% runs a value through a chain of one-argument lambdas, left to right. Each lambda's result is fed into the next.

set {_inc} to lambda (n: number) -> number:
    return {_n} + 1
set {_double} to lambda (n: number) -> number:
    return {_n} * 2

set {_out} to pipe 5 through {_inc}, {_double}
# inc(5) = 6, then double(6) = 12  ->  {_out} is 12

The lambdas come from a list, so you can build a chain up in a variable and reuse it. Anything in the list that isn't a lambda is skipped, so a stray non-lambda won't break the chain.

Added in 1.2.0.

Turning a function into a lambda

Already have a Skript function? You can wrap it in a lambda with function lambda "name". Now you can store it, pass it around, and call it like any other lambda.

function double(amount: number) :: number:
    return {_amount} * 2

set {_reward} to function lambda "double"

set {_x} to call lambda {_reward} with 5     # 10
run lambda {_reward} with 21                 # just runs it
add {_reward} to {_doublers::*}              # store it for later

The function is looked up by its name when the lambda runs. Arguments you pass are handed to the function in order.

Type hints

Skript has an experimental feature called type hints. When a script turns it on, Skript remembers what kind of value a local variable holds, and warns you at parse time when you use it in a way that makes no sense.

skLambda works with this. When you write set {_x} to lambda ...:, Skript now knows {_x} holds a lambda. Listeners work the same way (see Listeners).

Turn it on by putting using type hints at the very top of your script:

using type hints

command /testlambda:
    trigger:
        set {_double} to lambda (n: number) -> number:
            return {_n} * 2

        set {_result} to call lambda {_double} with 5
        send "Result: %{_result}%" to sender

        # This line is a mistake: you can't lowercase a lambda.
        # With type hints on, Skript catches it as a parse error.
        set {_bad} to {_double} in lowercase

Without using type hints, the bad line still loads (Skript treats the variable as "could be anything"); it just won't be caught early. Turning hints on is a nice way to catch slip-ups while you write.

This is a Skript feature, not a skLambda setting, so see Skript's own docs for the details. It's experimental, so it may change.

Using lambdas from Java

Since 1.5.0 a lambda simply is the common Java functional interfaces, so anything that wants one will take it as-is:

Consumer, BiConsumer, Function, Predicate, Supplier, Runnable, Comparator.

Which shape applies is decided by whatever is calling it, so the same lambda can be a consumer in one place and a predicate in the next. That makes it useful from a script too, through skript-reflect:

set {_stack} to (1 of writable book).getRandom()
set {_addPage} to lambda (meta: object):
    {_meta}.addPage("written by a lambda")
{_stack}.editMeta({_addPage})       # editMeta wants a Consumer

{_list}.removeIf({_predicate}), {_list}.sort({_comparator}) and {_optional}.map({_function}) all work the same way, with no adapter call and no wrapper section.

If you're writing a Java plugin, asPredicate(), asFunction(), asBiFunction(), asConsumer() and asSupplier() are also real methods on the lambda, for when you want a specific shape spelled out. Added in 1.1.0.

Working with skript-reflect

Anywhere skLambda takes a lambda, it also takes a skript-reflect section or a function reference. There is nothing to convert.

create new section with arguments variables {_n} stored in {_double}:
    return {_n} * 2

set {_doubled::*} to (1, 2, 3) mapped with {_double}      # 2, 4, 6
set {_f} to future of calling lambda {_double} with 21    # runs it off the main thread

A section takes its arguments through its own with arguments variables list, and whatever it returns is what comes back.

A function reference works too, and one written with arguments keeps them bound as the leading ones, exactly like with ... bound:

function add(a: number, b: number) :: number:
    return {_a} + {_b}

set {_add5} to the function "add" called with 5
set {_x} to call lambda {_add5} with 10                   # 15
set {_sum} to (1, 2, 3) reduced with (the function "add") # 6

Added in 1.5.0. None of this loads when skript-reflect isn't installed.

When are lambdas useful?

  • You want to save behavior in a variable and run it later.
  • You want a different bit of code to decide what runs.
  • You want to make small reusable helpers without making a full Skript function.

For using a lambda as a yes/no test, see Predicates. For running a lambda across a whole list, see List operations. For event listeners, see Listeners. For running a lambda off the main thread, see Async (the lambda body must be pure, with no Bukkit API).