The name in a constant definition, the iteration variable in an emitter in a for statement, or the name in a formal parameter declaration can be generalized to a destructuring rather than a simple name. It executes a constructor-like function call "in reverse" to bind names to parts of an object that would have been constructed by that function call.
The function call must be written in function(parameters) syntax. Due to syntatic ambiguity, it cannot be written in operator syntax. As a special exception, a list display [members] can be used as an abbreviation for list(members).
For example,
def [x, y, z] = f(a, b, c)expects f(a, b, c) to return a list of three members and binds x, y, and z to the members of the list.
The function invoked on the left-hand side, in this case list, must have a destructuring method defined, e.g. by defdestructure.
An actual parameter in a destructuring can itself be a function call rather than simply a name. In this case, the corresponding piece of the right-hand side will itself be destructured.
Named parameters can be used in a destructuring that calls a function (not list) that takes named parameters.
A rest parameter (trailing ...) can be used when destructuring certain functions (including list).
def function_call = value computes value and then defines constants whose names are the actual parameters of function_call and whose values are pieces of the result.
Variable definitions and assignment statements do not support destructuring.
The name of a formal parameter in a method or class definition can be a function call rather than simply a name. In this case, the corresponding actual parameter or default will be destructured when the method or constructor is called.
Generic formal parameters do not support destructuring.
The syntax of destructuring could have been defined by:
;; The result of parsing a destructuring constant: defclass destructuring(named: name name, ; the function positional list[name|destructuring], ; positional parameters named map[name, name|destructuring], ; named parameters rest name|destructuring|false) ; rest parameter or false ;; Definition of destructuring syntax defsyntax destructuring name [ "(" [ { [ keyword ] destructuring & "," }+ [ "..." ellipsis_flag ] ] ")" ] | "[" [ { destructuring & "," }+ [ "..." ellipsis_flag ] ] "]" => if not destructuring ;; Just a name, no parentheses after it name else def positional = stack([ d for d in destructuring, k in keyword if not k ]...) def named = { k.name => d for d in destructuring, k in keyword if k } def rest = ellipsis_flag and pop!(positional) destructuring(name: name or name(#list, modules.lunar), positional: list(positional...), named: named, rest: rest)
Destructuring behavior is defined using defdestructure which attaches to a function a destructuring method that accepts three parameters and returns a sequence of tokens or expressions, the same as a macro returns, which will be wrapped in a block. The destructuring method is called at compile time. A function's destructuring method is entirely separate from its regular methods.
The parameters are:
The result sequence is mainly composed of code returned by the function parameter. Other code, such as definitions of temporary variables, can be added.
The defdestructure statement takes two formal parameter lists. The first one is applicable to the actual parameter expressions of the function call on the left-hand side. The second one accepts the value and function parameters. If the name of the type of acceptable values is not the same as the name of the function, it can be specified using a type-suffix on the first parameter in the second formal parameter list; this does not specify the type of the formal parameter's value, which is always expression.
In the body of the defdestructure statement, the name context is in scope, similar to the body of defmacro. Its value is a context for creating temporary names that will be hygienically isolated from other code.
Destructuring is pre-defined for lists. The right-hand side can actually be any sequence, such as a list! or a stack. The left-hand side can be an invocation of the list function or a [...] list display which parses as an invocation of the list function.
For example, list destructuring could have been defined by
defdestructure list(members...) (rhs sequence, setter) def position = name("position", context) `assert $rhs.length = $(members.length) def $position := iterate($rhs) ` + for member in members, first? = true then false using append if not first? append `$position := iterate($rhs, $position) ` append setter(member, `next($rhs, $position)`)Thus
def [a, b] = f(x)would expand into something like
def temp = f(x) assert temp.length = 2 def position := iterate(temp) def a = next(temp, position) position := iterate(temp, position) def b = next(temp, position)where temp and position are hygienically invisible and assert, ., :=, iterate, and next are hygienically bound to their usual values regardless of any local bindings in scope.
A constructor expression for any user-defined type can be the left-hand side in destructuring if defdestructure has been done to attach a destructuring method to the constructor function or the type was defined by the simple form of defclass, which automatically defines destructuring of a constructor call to extract the slot values from an instance of the class.
Possible future feature: destructuring a string interpolation.
Previous page Table of Contents Next page