Pre-release Harn is pre-1.0 — the language, standard library, and CLI may change between releases. See the release notes

Destructuring patterns

Destructuring binds multiple variables from a dict or list in a single let, var, or for-in statement.

Dict destructuring

const {name, age} = {name: "Alice", age: 30}
// name == "Alice", age == 30

Each field name in the pattern extracts the value for the matching key. If the key is missing from the dict, the variable is bound to nil. Use _ as a discard binding when you want to ignore an extracted field:

const {name, debug: _} = {name: "Alice", debug: true}
// name == "Alice"; `_` is not bound

Default values

Pattern fields can specify default values with = expr syntax. The default expression is evaluated when the extracted value is nil (i.e. when the key is missing from the dict or the index is out of bounds for a list):

const { name = "workflow", system = "" } = { name: "custom" }
// name == "custom" (key exists), system == "" (default applied)

const [a = 10, b = 20, c = 30] = [1, 2]
// a == 1, b == 2, c == 30 (default applied)

Defaults can be combined with field renaming:

const { name: displayName = "Unknown" } = {}
// displayName == "Unknown"

Default expressions are evaluated fresh each time the pattern is matched (they are not memoized). Rest patterns (...rest) do not support default values.

List destructuring

const [first, second, third] = [10, 20, 30]
// first == 10, second == 20, third == 30

Elements are bound positionally. If there are more bindings than elements in the list, the excess bindings receive nil (unless a default value is specified). Use _ to discard positions without creating a binding:

const [_, second, _] = [10, 20, 30]
// second == 20; `_` is not bound

Field renaming

A dict pattern field can be renamed with key: alias syntax:

const {name: user_name} = {name: "Bob"}
// user_name == "Bob"

Rest patterns

A ...rest element collects remaining items into a new list or dict:

const [head, ...tail] = [1, 2, 3, 4]
// head == 1, tail == [2, 3, 4]

const {name, ...extras} = {name: "Carol", age: 25, role: "dev"}
// name == "Carol", extras == {age: 25, role: "dev"}

If there are no remaining items, the rest variable is bound to [] for list patterns or {} for dict patterns. The rest element must appear last in the pattern.

For-in destructuring

Destructuring patterns work in for-in loops to unpack each element:

const entries = [{name: "X", val: 1}, {name: "Y", val: 2}]
for {name, val} in entries {
  log("${name}=${val}")
}

const pairs = [[1, 2], [3, 4]]
for [a, b] in pairs {
  log("${a}+${b}")
}

_ is also a discard binding in loop patterns, so for [_, value] in ... or for (_, value) in ... drops the ignored element instead of binding it.

Var destructuring

var destructuring creates mutable bindings that can be reassigned:

let {x, y} = {x: 1, y: 2}
x = 10
y = 20

Discard bindings remain non-bindings under var as well: var [_, value] = still only introduces value.

Type errors

Destructuring a non-dict value with a dict pattern or a non-list value with a list pattern produces a runtime error. For example, let {a} = "hello" throws "dict destructuring requires a dict value".