A lambda function, also known as an anonymous function, is a function that is not bound to an identifier. You can create it within other functions, pass it as an argument, or return it from a function. Contrary to methods, lambda functions are not associated with any object.In practice, lambda functions allow you to define a function wherever you could store a value. For example, when connecting a signal, you need a function. You can define a lambda function directly in the connection call.
In Godot, all functions, be it methods, lambdas, and static functions, are objects with the Callable type. So when you read Callable in the documentation, it means a function.
Creating and using lambda functions
You can create a lambda function similar to any other function by using the func keyword inside a function. You can store the lambda function in a variable and call it later. Here is an example:
func_ready()->void:var multiply_by_ten :=func(number:int)->int:return number *10
To call a lambda function, you need to use the the Callable.call() method:
func_ready()->void:# ...var result := multiply_by_ten.call(5)print(result)# Prints "50"
In this example, the lambda function multiply_by_ten() is scoped to the _ready() function and cannot be accessed anywhere else.
How lambdas capture context
Lambdas in GDScript don't always evaluate variables from the surrounding context immediately. It depends on the type of variable you are accessing:
If you access a script-wide variable inside a lambda, the lambda will keep a reference to this variable. It will not evaluate the variable immediately; it will evaluate it when the lambda is called.
If you access a local variable in a lambda function, the lambda will evaluate the value immediately. It will not keep a reference to the variable to evaluate later because the variable will be destroyed when the function creating the lambda exits.
When accessing local variables, lambdas follow the same behavior as function parameters:
Objects, dictionaries, and arrays are passed by reference. They are not duplicated, and changes made to them inside the lambda will be reflected outside.
Numbers, strings, and booleans are passed by value. They are duplicated, and changes made to them inside the lambda will not be reflected outside.
Adding types and naming lambdas
You can define lambdas like any other function and give them type information all the same:
func_ready()->void:var multiply_by_ten :=func(number:int)->int:return number *10
NOTE:
The type hints in lambda definitions are not read by the editor in Godot 4.2, so you will get no autocomplete, in-editor errors, or return types when using lambdas. Hopefully, this will improve in future versions.
You can also name lambdas. When there is an error inside a lambda function, and you give it a name, you will see the function name in the error message. This can be helpful for debugging:
func_ready()->void:var multiply_by_ten :=funcmultiply_by_ten(number:int)->int:return number *10
When to use lambda functions
There are a few cases where lambdas are useful:
When you need a function only once.
When you need to customize behavior.
When you need to capture context.
When you need to create a function immediately.
Case 1: you need a use-once function
There are occasions where you know you will use a function only in that one occasion. For example, if you wanted to sort this array by age:
var characters :=[{"name":"Gimly","age":139},{"name":"Frodo Baggins","age":50},{"name":"Gandalf","age":2000},{"name":"Samwise Gamgee","age":33},]
You could do this:
func_sort_by_age()->bool:return a["age"]< b["age"]func_ready()->void:# sorting the characters by age characters.sort_custom(_sort_by_age)
This is fine, but has two issues:
It seems as if _sort_by_age could be used somewhere else, so we might forget to delete if we do not need it anymore.
When reading the code in _ready() and trying to understand what it does, you will need to jump to the function, understand it, then go back where you were. This is can create confusion; we call that indirection (in this example, it doesn't create confusion, but real code is often hundred of lines long).
It's probably better to write:
func_ready()->void:# Sorting the characters by age characters.sort_custom(func(a, b)->bool:return a["age"]< b["age"])
Now, if we don't want to sort the characters anymore, we can remove the entire function.Another common use is for signals. The example below assumes a counter with three buttons, +, -, and reset:
Lambdas are very practical to customize behavior. By having lambda properties, you can leave "holes" to fill from somewhere else.Here is a magical trap that does... something:
class_nameMagicalTrapextends Area2D
var action :=Callable()func_ready()->void: body_entered.connect(_on_body_entered)func_on_body_entered(body:CollisionObject2D)->void:if action.is_valid(): action.call(body)
One quirk of Godot's lambdas and bound functions is that you need to explicitly call them by using the call() method.
At runtime, you can now assign any lambda to action, as long as it respects the convention (receiving a node as its unique argument).Because lambdas can be passed around, you can also use them as function parameters:
var health :=10functake_damage(amount:int, special:Callable)->void: health -= amount
if health >0:if special.is_valid(): special.call(self)
Case 3: capturing context
Lambdas can use all the context that is around them, and capture it for use later.For example, provided your player has a method like in the previous paragraph:
var health :=10functake_damage(amount:int, special_effect:Callable)->void: health -= amount
if health >0:if special_effect.is_valid(): special_effect.call()
Then an enemy could trigger a "slow" type attack like so:
var strength :=1funcattack(player): player.take_damage(1,func(): player.speed = player.speed /2)
Using a lambda function groups the code together, avoiding fragmenting the logic across the script.Wrapping the function in () creates a function expression, which we immediately invoke by calling its .call() method. This pattern is commonly called Immediately Invoked Function Expression, or IIFE for short.This pattern is particularly useful if you need to declare one-time variables to calculate the value. Having them inside the function expression ensures they do not conflict with other scopes.