Keywords · LET

LET

A LET binds a name to the result of a single expression — reactive in a body, a constant at module or type level.

Format

The grammar of a single LET line:

LET ::= "LET" [ type ] identifier "=" ( expression | constantExpression )
type ::= name [ "(" type { " " type } ")" ]
expression ::= name { " " argument }
constantExpression ::= constant | name { " " constant }
argument ::= name | constant
constant ::= string | integer | number
name ::= identifier { "." identifier }
identifier ::= letter { letter | digit }
A LET keyword is followed by an optional type, then a name, and after the = either an expression — a function name applied to arguments — or a constant expression — a constant, or a type constructor applied to constants. An argument is itself a name or a constant, and a constant is a string, integer, or number.

Usage

A LET keyword can live at the Root, or inside FUNCTION, REVERSE, WITH, MODULE, and TYPE keywords.

At the Root

At the program's run root, a LET is a Root binding in the code that actually runs.

LET Maths.Real squared = Maths.Real.Multiply radius radius
LET Maths.Real area = Maths.Real.Multiply 3.14159 squared
Two bindings at the program's run root, each feeding the next.

The type comes first, then the name, then the expression. Change radius and both bindings settle again, in order.

Inside a FUNCTION

A derived binding in the function body, recomputed reactively as its inputs change.

FUNCTION Maths.Real Area
  INPUT Maths.Real radius
  LET Maths.Real squared = Maths.Real.Multiply radius radius
  LET Maths.Real area = Maths.Real.Multiply 3.14159 squared
A function that derives a circle's area from its radius through a chain of bindings.

Each LET names one step of the body, and the compiler recomputes a binding only when a value it read has changed.

Inside a REVERSE

Part of a write-back body, preparing the value that the closing SET pushes back.

REVERSE Ui.Form.Result submissionReverse
  LET Ui.Form.Result result = Api.Client.ParseFormResult httpResponse
  SET formDataState = result
A write-back whose LET parses the value the closing SET pushes into outer state.

The LET bindings compute from the incoming value exactly as in a forward block, preparing what the final SET writes back.

Inside a WITH

A binding in a WITH block, computed with the swapped module implementation in place.

WITH Logic = Ui.Logic
  LET Ui.Element panel = Ui.Content.Panel content
A binding inside a module override, resolving Logic to Ui.Logic.

The LET runs under the override, so every name it reaches for as Logic is served by the replacement until the block ends.

Inside a MODULE

At module level a LET declares a named constant — a fixed value in the module's namespace, not running code.

MODULE Palette
  LET Colour.RGB Brand = Colour.RGB 0.1 0.4 0.9
A constant colour declared in a module's namespace.

The expression must be a constant — a literal, or a type constructor applied to literals — so the value is fixed once, with no inputs or reactive dependencies. It is reached through the module's name, as Palette.Brand.

Inside a TYPE

A type doubles as a namespace, so a LET inside it declares a constant reached through the type's name.

TYPE Colour.RGB
  FIELD Maths.Real red
  FIELD Maths.Real green
  FIELD Maths.Real blue
  LET Colour.RGB Black = Colour.RGB 0.0 0.0 0.0
A named colour constant inside the RGB type, reached as Colour.RGB.Black.

The constant follows the type's FIELD lines and is named through the type — here Colour.RGB.Black. As at module level, its value must be a constant expression.

Can Contain

A LET is a leaf: it takes a type, a name, and a single expression on the right of the =. In a body that is normally one call with plain-value arguments, recomputed reactively; at MODULE or TYPE level it must be a constant — a literal or a type constructor applied to literals. Either way it opens no indented lines and contains no further keywords.

Targets

The same example, rendered to each target.

JavaScript

var squared = Maths.Real.Multiply(radius, radius);
var area = Maths.Real.Multiply(3.14159, squared);

TypeScript

const squared = Maths.Real.Multiply(radius, radius);
const area = Maths.Real.Multiply(3.14159, squared);

Dart

final squared = Maths_Real.Multiply(radius, radius);
final area = Maths_Real.Multiply(3.14159, squared);

Swift

let squared = Maths.Real.Multiply(radius, radius)
let area = Maths.Real.Multiply(3.14159, squared)

Go

squared := Maths_Real.Multiply(radius, radius)
area := Maths_Real.Multiply(3.14159, squared)

Rust

let squared = cubelogix_runtime::real_multiply(radius, radius);
let area = cubelogix_runtime::real_multiply(3.14159, squared);

Zig

const squared = maths.real.multiply(radius, radius);
const area = maths.real.multiply(3.14159, squared);

JSON

["=", "squared", "Maths.Real", ["Maths.Real.Multiply", [], ["radius", "radius"]]],
["=", "area", "Maths.Real", ["Maths.Real.Multiply", [], [["#", "", "Maths.Real", "3.14159"], "squared"]]]

Related

A changeable cell is a STATE; a function returns its last value with a trailing = (see FUNCTION), and a REVERSE may use LET lines before it writes back. The reactive model is covered in Reactive. Back to the language.