Destructuring
A local or let declaration can bind a table's keys or an array's positions directly to named bindings.
Table by key, array by position
let { a, b } = table binds a and b from the keys of the same name. let [a, b] = array binds them from positions 0 and 1. local destructures the same way. Commas between fields are optional in both forms.
let loadout = { weaponName = "mosin", ammoBelt = 30 }
let { weaponName, ammoBelt } = loadout
println($"{weaponName}: {ammoBelt} rounds")
let spawnPoint = [120, 5, -40]
let [x, y, z] = spawnPoint
println($"spawn at ({x}, {y}, {z})")mosin: 30 rounds
spawn at (120, 5, -40)Defaults, and a key with no default
let { a = 0 } = table binds a from the key a. It uses the default expression only if that key is missing. An array pattern takes a default the same way for a missing position. A field with no default throws if its key or position is missing; it does not become null.
let loadout = { weaponName = "mosin" }
let { weaponName, ammoBelt = 0 } = loadout
println($"{weaponName}: {ammoBelt} rounds")
// a missing key with no default throws instead of yielding null
try {
let { reserveBelt } = loadout
}
catch (e) {
println("let { reserveBelt } = loadout throws:", e)
}mosin: 0 rounds
let { reserveBelt } = loadout throws: the index 'reserveBelt' (type='string') does not existA default applies only when the key or position is missing. A key that is present and holds null is not missing: it binds null, and the default is not used.
Type hints in a pattern
A field may carry a type hint, written the same way as a parameter annotation. A hint and a default can appear together, in that order.
let vehicle = { hitPoints = 250, crew = 4 }
// a field may carry a type hint, a default, or both
let { hitPoints : int, crew : int = 2, ammoBelt : array = [] } = vehicle
println($"{hitPoints} hp, crew {crew}, {ammoBelt.len()} belts")
// the hint is checked against what actually arrives
let damaged = { armor = "thick" }
try {
let { armor : int } = damaged
println("armor =", armor)
} catch (e) {
println("let { armor : int } = damaged throws:", e)
}250 hp, crew 4, 0 belts
let { armor : int } = damaged throws: type 'string' differs from the declared type 'int'The hint is checked against the value that arrives. A mismatch throws type 'string' differs from the declared type 'int'. Because a present null is not missing, it reaches the hint and the hint rejects it: { crew : int = 2 } does not turn a null crew into 2. See Type annotations.
There is no rename syntax
Unlike JavaScript, a table pattern cannot bind a key under a different name. { key } always binds a variable named key; writing = after a field name supplies its default, not an alias:
// this does NOT rename hitPoints to hp - it tries to use an undeclared
// variable named hp as hitPoints's default, and fails to compile
let { hitPoints = hp } = vehicle
To bind under a different name, destructure first and then assign: let { hitPoints } = vehicle; let hp = hitPoints. Only the from "module" import statement below renames directly, with as.
A pattern is one level deep
A field inside { } or [ ] is always a plain name, with an optional type hint and default. It is never another { } or [ ] pattern. There is no nested destructuring. To reach into a nested table or array, destructure one level, then destructure the result:
let mission = { squad = { name = "alpha", spawnPoint = [10, 0, 5] } }
// a pattern can only be one level deep, so unpack the outer key first...
let { squad } = mission
// ...then destructure what came out of it
let { name, spawnPoint } = squad
let [x, y, z] = spawnPoint
println($"{name} spawns at ({x}, {y}, {z})")alpha spawns at (10, 0, 5)Destructuring in a from-import
from "module" import a, b as c imports specific exports of a module by name. An optional as binds an export under a different local name. * imports every export. import "module" binds the whole module under one name. Most game code uses the from form to import a few names from a shared module.
from "math" import sqrt, pow as raisePow
println("sqrt(81) =", sqrt(81))
println("raisePow(2, 8) =", raisePow(2, 8))sqrt(81) = 9
raisePow(2, 8) = 256Destructuring in a foreach
A foreach binder accepts a pattern, so the fields of each element can be bound directly. An index or key may still come first, and an array element destructures by position.
let squads = [
{ name = "alpha", strength = 4 },
{ name = "bravo", strength = 9 },
]
// the loop binder is a pattern, so the fields are bound directly
foreach ({ name, strength } in squads)
println($"{name} fields {strength}")
// an index may still come first
foreach (slot, { name } in squads)
println($"slot {slot}: {name}")
// an array element destructures by position
let spawnPoints = [[10, 20], [30, 40]]
foreach ([east, north] in spawnPoints)
println($"spawn at {east},{north}")alpha fields 4
bravo fields 9
slot 0: alpha
slot 1: bravo
spawn at 10,20
spawn at 30,40Where else a pattern is accepted
A pattern is accepted in a let/local declaration, in a foreach binder, in a from ... import list, and in a function parameter list; see Functions. It is not accepted anywhere else. A bare { name, strength } = squad assignment does not parse, because a pattern only introduces new bindings.