Callables and lambdas

Six operations kept in an array as Callables, five lambdas and one method with bind, each one handed to Array.map over the same numbers.

The editor above is running this project. Change a line and it reloads.

Godot classes used

CallableCallable.bindArray.map

The code

scripts/main.gd
extends Node

const SOURCE: Array = [3, 8, 1, 6, 9, 4, 7, 2]
const TICK: float = 0.45

var operations: Array = []
var rows: Array = []
var buttons: Array = []
var status: Label
var elapsed: float = 0.0
var active: int = 0


func _ready() -> void:
	operations = [
		{name = 'double', fn = func(value: int) -> int: return value * 2},
		{name = 'square', fn = func(value: int) -> int: return value * value},
		{name = 'halve', fn = func(value: int) -> int: return roundi(value * 0.5)},
		{name = 'mirror', fn = func(value: int) -> int: return 10 - value},
		{name = 'cap at five', fn = func(value: int) -> int: return mini(value, 5)},
		{name = 'add bonus, bound', fn = _add_bonus.bind(12)},
	]

	var row: HBoxContainer = _root()
	_menu(row)
	_table(row)
	_apply(0)

	print('every entry in the list is a Callable, five lambdas and one method with bind')


func _process(delta: float) -> void:
	elapsed += delta

	if elapsed < TICK:
		return

	elapsed = 0.0
	_apply((active + 1) % operations.size())


func _add_bonus(value: int, bonus: int) -> int:
	return value + bonus


func _apply(index: int) -> void:
	active = index
	var operation: Dictionary = operations[index]
	var result: Array = SOURCE.map(operation.fn)
	var ceiling: int = maxi(int(result.max()), 1)

	for i in SOURCE.size():
		var line: Dictionary = rows[i]
		line.result.text = str(result[i])
		line.bar.max_value = ceiling
		line.bar.value = result[i]

	for i in buttons.size():
		buttons[i].add_theme_color_override('font_color', Color('#8eef97') if i == index else Color('#8a90a0'))

	status.text = 'SOURCE.map(operations[%d].fn) gave %s' % [index, str(result)]


func _menu(row: HBoxContainer) -> void:
	var column: VBoxContainer = VBoxContainer.new()
	column.custom_minimum_size = Vector2(300, 0)
	column.add_theme_constant_override('separation', 8)
	row.add_child(column)

	var title: Label = Label.new()
	title.text = 'the list of Callables'
	title.add_theme_font_size_override('font_size', 22)
	title.add_theme_color_override('font_color', Color('#8da5f3'))
	column.add_child(title)

	for i in operations.size():
		var button: Button = Button.new()
		button.text = operations[i].name
		button.pressed.connect(_apply.bind(i))
		column.add_child(button)
		buttons.append(button)


func _table(row: HBoxContainer) -> void:
	var column: VBoxContainer = VBoxContainer.new()
	column.custom_minimum_size = Vector2(660, 0)
	column.add_theme_constant_override('separation', 8)
	row.add_child(column)

	var title: Label = Label.new()
	title.text = 'the same eight numbers, mapped'
	title.add_theme_font_size_override('font_size', 22)
	title.add_theme_color_override('font_color', Color('#8eef97'))
	column.add_child(title)

	for value in SOURCE:
		var line: HBoxContainer = HBoxContainer.new()
		line.add_theme_constant_override('separation', 14)
		column.add_child(line)

		var source: Label = Label.new()
		source.text = str(value)
		source.custom_minimum_size = Vector2(34, 0)
		source.add_theme_color_override('font_color', Color('#8a90a0'))
		line.add_child(source)

		var arrow: Label = Label.new()
		arrow.text = '->'
		arrow.add_theme_color_override('font_color', Color('#8a90a0'))
		line.add_child(arrow)

		var result: Label = Label.new()
		result.custom_minimum_size = Vector2(48, 0)
		result.add_theme_font_size_override('font_size', 20)
		line.add_child(result)

		var bar: ProgressBar = ProgressBar.new()
		bar.show_percentage = false
		bar.custom_minimum_size = Vector2(440, 18)
		bar.add_theme_stylebox_override('background', _bar_style(Color('#2a2f3d')))
		bar.add_theme_stylebox_override('fill', _bar_style(Color('#8da5f3')))
		line.add_child(bar)

		rows.append({result = result, bar = bar})


func _bar_style(color: Color) -> StyleBoxFlat:
	var style: StyleBoxFlat = StyleBoxFlat.new()
	style.bg_color = color
	style.set_corner_radius_all(5)

	return style


func _root() -> HBoxContainer:
	var background: ColorRect = ColorRect.new()
	background.color = Color('#12141a')
	add_child(background)
	background.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)

	var margin: MarginContainer = MarginContainer.new()

	for side in ['margin_left', 'margin_right', 'margin_top', 'margin_bottom']:
		margin.add_theme_constant_override(side, 48)

	add_child(margin)
	margin.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)

	var column: VBoxContainer = VBoxContainer.new()
	column.add_theme_constant_override('separation', 16)
	margin.add_child(column)

	var title: Label = Label.new()
	title.text = 'A function kept in a variable'
	title.add_theme_font_size_override('font_size', 28)
	column.add_child(title)

	var hint: Label = Label.new()
	hint.text = 'Nothing below knows what the operation is. It receives a Callable and hands it to Array.map.'
	hint.add_theme_color_override('font_color', Color('#8a90a0'))
	column.add_child(hint)

	var row: HBoxContainer = HBoxContainer.new()
	row.add_theme_constant_override('separation', 36)
	column.add_child(row)

	status = Label.new()
	status.add_theme_font_size_override('font_size', 18)
	status.add_theme_color_override('font_color', Color('#8eef97'))
	column.add_child(status)

	return row

More Data examples