A number of Nex’s surface forms are conveniences: they add no expressive power, and are explained by translation into a smaller bare language. Defining them this way keeps the semantic rules of Chapters 4 and 5 few, since a derived form inherits its meaning from its translation. This appendix gives those translations.
Each translation is shown as a surface form, an arrow, and the bare form it
is translated to: wherever the phrase on the left of the arrow appears, it is
replaced by the phrase on the right. Translation happens before elaboration, so
the static and dynamic semantics see only bare forms. The fresh identifiers a
translation introduces, written _i, _c and the like,
are chosen to differ from every identifier in the program.
C.1Collection Displays
The array, map, and set displays abbreviate a creation followed by a sequence of insertions.
[e₁, …, eₙ] | → | do let _a := create Array; _a.add(e₁); … ; _a.add(eₙ); _a end |
{k₁: v₁, …, kₙ: vₙ} | → | do let _m := create Map; _m.put(k₁, v₁); … ; _m end |
{} | → | create Map |
#{e₁, …, eₙ} | → | do let _s := create Set; _s.add(e₁); … ; _s end |
#{} | → | create Set |
The element type of the created collection is inferred from the display,
or taken from the annotation of the surrounding let or
parameter when the display is empty. The empty set must be written
#{} and not {}: the latter is the empty map.
(A do…end used here is the
block-as-expression whose value is its final expression. The displays are
the one place the bare language needs it, and it may be read simply as
“build the collection and yield it.”)
C.2The Counted Loop
The repeat loop runs its body a number of times given by its
bound. It translates into a general loop over a fresh counter.
repeat e do b end | → | |
from let _i := 0 until _i >= e do b ; _i := _i + 1 end | ||
The bound e appears in the until condition and so is
re-evaluated before each iteration; an assignment within the body to a
variable occurring in e therefore alters the number of repetitions. A
program that requires a count fixed in advance should bind the bound first
(let n := e) and repeat over n.
C.3The Cursor Loop
The across loop traverses any Iterable value
(Appendix B), or any value of a class with a cursor method that
returns a cursor. It translates into a general loop driven by the
Cursor methods.
across e as x do b end | → | |
do let _c := e.cursor; _c.start; | ||
from until _c.at_end do let x := _c.item ; b ; _c.next end end | ||
The cursor variable x is bound afresh on each iteration to the
current item, and is in scope only within the body. Its type is that of
e.cursor.item: T when e is an
Iterable[T]. When e is itself a cursor, it is the type
of e.item, so a cursor class that declares a narrower
item keeps it. An Array yields its elements in order,
a String its characters, and a Map its entries as
Map_Entry values with a key and a value
(Section 2.8 and Appendix B).
C.4Cascaded Conditionals
The elseif chain abbreviates nested conditionals.
if e₁ then b₁ elseif e₂ then b₂ else b₃ end | → | |
if e₁ then b₁ else (if e₂ then b₂ else b₃ end) end | ||
C.5Shared Parameter Types
Several parameters may share a single type annotation; this abbreviates the fully annotated list.
(a, b: T) | → | (a: T, b: T) |
C.6Union Declarations
A union declaration (Section 3.2.2) abbreviates a sealed
hierarchy of data classes. The parent becomes a sealed deferred class, and each
variant becomes a class inheriting it, whose fields are the variant’s
payload and whose sole constructor takes and assigns them.
union P⟨[G…]⟩ V₁ … Vₙ end | → | |
sealed deferred class P⟨[G…]⟩ end | ||
and, for each variant Vᵢ(f₁: T₁, …, fₖ: Tₖ), | ||
class Vᵢ inherit P⟨[G…]⟩ feature f₁: T₁ … fₖ: Tₖ | ||
create make(f₁: T₁, …, fₖ: Tₖ) do f₁ := f₁ ; … ; fₖ := fₖ end end | ||
A payload-free variant Vᵢ becomes a class with an empty
feature section and a nullary make. Because the translation
names the parent in each variant’s inherit clause, the
generic arguments carry through, and the resulting classes are exactly
those a hand-written sealed hierarchy would produce. Nothing further
— no method, invariant, or contract — is generated.
Prefixing the declaration with enum enriches the same
hierarchy into an enumeration. This is permitted only when every
variant is payload-free and P is non-generic. On top of the
translation above, the parent gains an ordering and one interned constant
per member, and each variant’s make records its
declaration index.
enum union P V₁ … Vₙ end | → | |
sealed deferred class P inherit Comparable | ||
feature ordinal: Integer | ||
set_ordinal(o: Integer) do ordinal := o end | ||
compare(o: Any): Integer do | ||
if convert o to p: P then result := ordinal − p.ordinal | ||
else raise "P.compare requires a P" end | ||
end | ||
feature V₁: P = create V₁.make() ; … ; Vₙ: P = create Vₙ.make() | ||
values: Array[P] = [V₁, …, Vₙ] end | ||
and, for each variant Vᵢ at 0-based declaration index i, | ||
class Vᵢ inherit P create make() do this.set_ordinal(i) end end | ||
Thus P.Vᵢ is the one canonical Vᵢ value: a class
constant, evaluated once (Section 3.3), so P.Vᵢ == P.Vⱼ
tests identity. The members order by the sequence in which they are
written, and P.values lists them all. compare takes an
Any, as Comparable’s does, since an override may not
narrow a parameter (Section 4.9); given a value that is not a
member of P, it raises.
Because the enrichment claims those four names, a variant may not be
named ordinal, set_ordinal, compare,
or values.
Each variant’s constructor reaches ordinal through
set_ordinal rather than assigning it directly, since it is a
field P declares, not one Vᵢ does. The field assignment
rule of Section 4.5 confines a field write to its own declaring class regardless of
receiver, so Vᵢ could not assign it any other way — bare or
otherwise.
A plain union never gains any of this; the enrichment is
requested, never inferred.
C.7Match Patterns and Guards
A match clause richer than the bare
id as id dispatch of the core
(Section 2.8) is translated into it. A clause
V(fp₁, …, fpₙ) as x if g then b becomes the bare clause
V as _m — binding x to _m as
well when as x is present — whose body binds the field
patterns and then tests the field constraints together with the guard. If
any test fails, control passes to the clauses that follow, exactly as a
false guard does.
Each field pattern fpᵢ on the field f contributes a
binding, a test, or both:
f (bind) | → | let f := _m.f |
f as y (rename) | → | let y := _m.f |
_ (skip) | → | — nothing |
f: T (type test) | → | test convert _m.f to _n: T, then let f := _n |
f: T(…) (nested) | → | test convert _m.f to _n: T, then T’s field patterns on _n |
The tests are conjoined with the guard g. A clause whose
field patterns are only binds, renames, and skips contributes no test, so
once its class matches it commits and never falls through — which is
why such a clause still counts toward exhaustiveness (Section 4.5),
whereas a clause carrying a type test, a nested pattern, or a guard does not.
The one exception is a type test f: T that cannot fail because
the declared type of f already conforms to T; its
convert still runs, but the clause counts as if it were a plain
bind.
A top-level _ pattern translates to the else
block.
C.8Expression Functions
An anonymous function whose body would only compute its result may be
written with => and the expression. The expression extends as
far to the right as an expression can.
fn (ps): T => e | → | fn (ps): T do result := e end |
Unlike the other forms here, the translation is completed during
elaboration, by the rule for anonymous functions in Section 4.4: when the
result type T is neither written nor supplied by the context, it
is the type of e; and when the result type is Void, or
e has no value (a call to a procedure), the body is e
itself, as a statement, rather than result := e.
let double := fn (n: Integer) => n * 2 -- Function(n: Integer): Integer
names.sort(fn (a, b) => a.length - b.length) -- a, b: String, from Array[String].sort
let say := fn (m: String) => print(m) -- no result
C.9Extensions of Classes
An extension whose target is a class (Section 3.2.3) abbreviates editing that class’s declaration: its feature sections are appended to the class’s own.
class C⟨[G…]⟩ … S₁ … Sₖ ⟨invariant⟩ end | ||
extend C⟨[G…]⟩ X₁ … Xₘ end | → | |
class C⟨[G…]⟩ … S₁ … Sₖ X₁ … Xₘ ⟨invariant⟩ end | ||
Several extensions of one class are appended in the order in which the static world receives them: those of interned units first, as the units are elaborated, then the current unit’s, in source order. The order affects no meaning, since an extension may not supply a feature the class or another extension supplies.
The translation is not quite textual. Each routine appended this way keeps the knowledge that it came from an extension, for two purposes. Its body is elaborated with a client’s view of the class (Section 4.10), not the class’s own; and a diagnostic about it names the extension’s unit and line.
C.10What Is Not Derived
It is worth saying which apparent conveniences are not derived forms, but primitive constructs with rules of their own.
The conditional expression
when…then…else is
primitive (Section 4.4), not an abbreviation for an if
statement, because it yields a value.
The short-circuit operators and, or and
implies are primitive (Section 5.3), not abbreviations
for conditionals, because their evaluation order is part of the language’s
definition. a implies b has the value of not a or b
but is not an abbreviation for it either: its right operand is checked with the
narrowings of a in effect, as an and’s is
(Section 4.5), and an or’s is not.
The safe access ?. is primitive too (Sections 4.4
and 5.3).
The contract clauses, though they could in principle be expanded into explicit checks and raises, are treated as primitive so that the Definition can speak of preconditions, postconditions, and invariants as first-class notions (Section 5.6) rather than as a particular pattern of generated code.
An extension of a built-in type or a host class is primitive too (Sections 4.10 and 5.4). Such a type has no declaration for the extension to be folded into, and the features it adds are reached by rules of their own rather than as methods of a class.