-
Notifications
You must be signed in to change notification settings - Fork 0
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.
set {_double} to lambda (n: number) -> number:
return {_n} * 2What this says:
-
lambdastarts a lambda. -
(n: number)means it takes one argument calledn, which must be a number. -
-> numbermeans it gives back a number. -
return {_n} * 2is the value it gives back,n * 2.
Inside the body, the arguments become local variables: n becomes {_n}.
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}If the lambda takes no input, leave off the ( ... ).
set {_now} to lambda -> number:
return unix timestamp of nowIf 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 opThis is handy for short lambdas. The body can be one of three things:
- A condition (like
{_p} is op). The lambda gives backtrueorfalse. 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} * 2Returning 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.
There are two ways to use a lambda after you have one.
set {_x} to call lambda {_double} with 5
# {_x} is now 10There 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 5Pick 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 7run lambda {_greet} with playerrun 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.
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, bronzeAdded in 1.5.0. Before, a list return failed with a confusing parse error and then handed back nothing.
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 # 15Only 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.
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 15The inner lambda remembers {_n} from the outer one. So {_plus5} is now a lambda that always adds 5.
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 120The 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.
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 # 120Three 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.
%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 # 105Bind 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} # 42Added in 1.1.0. For flipping a predicate's result, see negated.
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 12The 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.
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 laterThe function is looked up by its name when the lambda runs. Arguments you pass are handed to the function in order.
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 lowercaseWithout 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.
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.
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 threadA 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") # 6Added in 1.5.0. None of this loads when skript-reflect isn't installed.
- 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).
Guides
Reference





