See all glossary terms

Lambda Function

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.
@onready var button: Button := %Button

func _ready() -> void:
	button.pressed.connect(
		func() -> void:
			print("Button pressed!")
	)
NOTE:
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:
  1. 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.
  2. 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:
  1. Objects, dictionaries, and arrays are passed by reference. They are not duplicated, and changes made to them inside the lambda will be reflected outside.
  2. 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 := func multiply_by_ten(number: int) -> int:
		return number * 10

When to use lambda functions

There are a few cases where lambdas are useful:
  1. When you need a function only once.
  2. When you need to customize behavior.
  3. When you need to capture context.
  4. 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:
  1. 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.
  2. 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:
var count := 0

@onready var button_plus: Button := %ButtonPlus
@onready var button_minus: Button := %ButtonMinus
@onready var button_reset: Label := %ButtonReset
@onready var label_counter: Label := %LabelCounter

func _ready() -> void:

	button_reset.pressed.connect(
		func() -> void:
			count = 0
			label_counter.text = "0"
	)

	button_plus.pressed.connect(
		func() -> void:
			count += 1
			label_counter.text = str(count)
	)

	button_minus.pressed.connect(
		func() -> void:
			count -= 1
			label_counter.text = str(count)
	)

Case 2: customizable behaviors

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_name MagicalTrap extends 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)
Why did you use call()?
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 := 10

func take_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 := 10

func take_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 := 1

func attack(player):
	player.take_damage(1,
		func():
			player.speed = player.speed / 2
	)
NOTE:
For more details on how lambdas capture context, jump to How lambdas capture context.

Case 4: immediately invoked function expressions

There are cases where a variable requires some processing to be generated.
In those cases, instead of doing something like:
var largest_target: Node2D = null

# ... other variables here

func _ready() -> void:
	for target in get_children():
		if largest_target == null or target.size > largest_target.size:
			largest_target = target
You could group the logic in a lambda function and immediately call it:
@onready var largest_target := (func() -> Node2D:
	var largest_target := null
	for target: Node2D in get_children():
		if largest_target == null or target.size > largest_target.size:
			largest_target = target
	return largest_target).call()
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.