Before any user code is elaborated, an environment already exists: a collection of classes and values that the Definition presupposes and every program may use. This appendix records that standard environment.
It is not a complete library reference — for that, see the Nex Reference — but the part of the standard environment on which the semantics of earlier chapters depends.
B.1The Root and the Foundational Classes
Every class conforms ultimately to Any, the root of the class
hierarchy, whose type is the type Any
(Section 4.1). A user class may write inherit Any explicitly,
but does so implicitly in any case.
| Class | Method | Signature | Meaning |
|---|---|---|---|
Any | to_string | returns String | user-facing rendering |
equals | takes Any, returns Boolean | value equality, used by =; overridable | |
hash | returns Integer | hash code consistent with equals, by which Map and Set place keys and elements; overridable | |
clone | returns Any | copy; collections override with deep copy |
Three further foundational classes are in the standard environment:
Function—the base class of all closures, whose type is the unconstrained function typeFunction. It supplies the invocation protocolcall0,call1, …, by which a closure of any arity up to a fixed bound is applied.Cursor—the abstract iteration interface, with methodsstart,item,next, andat_end. Theacrossloop is defined in terms of this protocol (Appendix C).Comparable(deferred)—withcompare, which takes anAnyand returns a negative, zero or positiveIntegeraccording to the order of the two values. It is the basis of the comparison operators on ordered values.
Hashing needs no class of its own: hash belongs to
Any, so every value has it, and a value of any type may be a
Map key or a Set element.
B.2Scalar Classes
Of the type names the lexer recognises (Section 2.1), five denote scalar
classes of the standard environment: Integer, Real,
Char, Boolean, and String. Three more scalar
classes, Byte, Integer16, and Integer32, are named by
ordinary identifiers. Each inherits
Comparable, and it is through that class —
together with the arithmetic of the numeric classes — that the operators of
Section 2.6 are given meaning on scalar values.
| Type | Values | Notes |
|---|---|---|
Integer | 64-bit integers | literals of int form; default numeric type; also written Integer64 |
Byte | 8-bit unsigned integers, from 0 to 255 | literals of byte form (200u8); element type of to_bytes |
Integer16 | 16-bit signed integers | literals of int16 form (8080i16) |
Integer32 | 32-bit signed integers | literals of int32 form (70000i32) |
Real | floating-point | literals of real form; Real / Integer yields Real |
Char | characters | literals of char form |
Boolean | true, false | operand and result of the logical operators |
String | text | literals of string form; iterates by character |
The arithmetic operators apply to numeric operands, returning the join of
their types (Section 4.3): Real if either is, and Integer otherwise. An
integer operand is admitted where a real is required, so total / count with
total of type Real yields a Real; and an operand of type
Byte, Integer16, or Integer32 is taken as the
Integer of the same value, so the result is an Integer.
The comparison operators apply to Comparable operands
through compare. Scalars are immutable and unstored, so
identity equality == coincides with value equality
= upon them (Section 5.3).
B.3Scalar Value Spaces
The previous section names the scalar classes; this one fixes the
value spaces they denote — their ranges, their numeric
formats, and the character model of Char and
String.
The guiding decision here is worth stating plainly: the numeric tower is pinned down exactly and identically on every platform. The width of an integer, the behaviour of arithmetic on overflow, the format of a floating-point number, and the result of division are all fixed by this Definition, not left to the host.
Nex can compile to more than one platform — the Java virtual machine at present. An earlier edition left the integer width and overflow behaviour host-defined, so the same program could denote different values on different back ends. That latitude is withdrawn: a program’s arithmetic now means one thing on every conforming implementation, and a program may rely on it.
What remains host-defined is only what is genuinely internal and unobservable through the language, and never the result of an arithmetic operation.
Integer
Integer is a signed two’s-complement integer of
exactly 64 bits, with range −263 to 263 − 1, on
every platform.
Its arithmetic is checked: an operation (+,
-, *, unary -, or ^)
whose mathematical result lies outside that range does not wrap, widen, or
lose precision — it raises an
Arithmetic_Overflow exception.
Integer division / truncates toward zero, and the
remainder % is the truncated remainder, taking the
sign of the dividend, so a = (a / b) * b + (a % b) holds
whenever the quotient exists: -7 / 2 is -3,
-7 % 3 is -1, and 7 % -3 is
1. (This is the convention of C and Java, not the floored
convention of Python.)
Division and remainder by zero have no integer result, and
raise a Division_by_Zero exception. The one
division whose mathematical result lies outside the range, the smallest
Integer (−263) divided by -1, raises Arithmetic_Overflow
like any other overflow.
This is uniform across back ends: where a host’s native integers
are wider or narrower than 64 bits, the implementation carries
Integer as a 64-bit value and checks each operation, so a
program’s integer arithmetic denotes the same value, or raises the
same exception, everywhere.
The bitwise operations of Integer
(bitwise_left_shift and its companions) operate on the low
32 bits, with bit 0 the least significant. They are
the one part of the integer model that is not 64-bit, and they too behave
identically on every platform. An integer literal that does not fit the
64-bit range is rejected (Section 2.2).
Real
Real is an IEEE 754 double-precision binary
floating-point value on every platform, in both its representation and
its arithmetic. A real literal denotes the nearest representable double, which may
not be the decimal written, so that 0.1 + 0.2 evaluates to
0.30000000000000004 and not to 0.3.
Division follows IEEE 754: 1.0 / 0.0 yields
positive infinity, -1.0 / 0.0 yields negative infinity, and
0.0 / 0.0 yields NaN — none of these raises.
The remainder % on reals is likewise truncated (the
fmod of C): the result has the sign of the dividend, so
-7.5 % 2.0 is -1.5, and a real remainder by zero
is NaN, not an exception.
Every ordering comparison (<, <=,
>, >=) against NaN is false, as
IEEE 754 prescribes. The special values positive and negative
infinity and NaN are ordinary Real values, produced by
arithmetic and compared by the IEEE rules — NaN is unequal
to every value including itself. The standard environment provides
is_nan, is_infinite, and is_finite to
inspect them.
This is the deliberate asymmetry of the numeric tower: integer division by zero raises, because there is no integer to return, while real division by zero is the IEEE value. It is the split drawn by most languages that carry both an integral and a binary floating type, and it is fixed here on every platform rather than left to the host.
Byte, Integer16 and Integer32
Byte, Integer16, and Integer32 are integers of an
exact width, for data whose layout the program does not choose, such as file
contents and network messages. Byte is unsigned, with range 0 to 255.
Integer16 and Integer32 are signed two’s-complement, with ranges
−215 to 215 − 1 and −231 to 231 − 1. Their value spaces are fixed on every platform.
Each is a class in its own right. No conversion between any two of
Integer, Byte, Integer16, and Integer32 is
implicit, in either direction (Section 4.3). The methods
to_byte, to_integer16, to_integer32, and
to_integer convert explicitly: a conversion to a type whose range does not contain
the value raises, and one to a wider type always succeeds. A value of a
fixed-width type is equal only to a value of its own type with the same number.
Arithmetic on these types is arithmetic on Integer: each operand is taken as the
Integer of the same value and the result is an Integer (Section 5.3),
so it is subject to the checked-arithmetic rules above and cannot overflow the narrow type.
Their ordering and equality operators apply to two values of the same type.
Their bitwise methods (bitwise_and, bitwise_or,
bitwise_xor, bitwise_not, the shifts, the rotations, and
bitwise_is_set, bitwise_set, bitwise_unset) operate on
the whole width of the type — 8, 16, or 32 bits, with bit 0 the least significant —
and yield a value of the same type. Right shift of a signed type copies the sign bit, and the
logical right shift shifts in zeros; results wrap to the width, so 32767i16.bitwise_left_shift(1)
is -2i16. A bit index must be at least 0 and less than the width of the type in bits, and a shift count
must be non-negative (a count equal to the width or more shifts every original bit out, leaving zeros, or copies of the sign bit for an arithmetic right shift of a negative value); an argument outside these bounds
raises. abs of the minimum value of a signed type, which has no positive
counterpart, raises. (The bitwise methods of Integer keep their 32-bit behaviour, above.)
Boolean and the character model
Boolean has exactly the two values true and
false.
A Char is a single Unicode code point. A
character constant may be written as a literal character, as one of the
named characters of Section 2.2, or as a decimal code point after
#, so #65 denotes the same character as
#A.
A String is a finite sequence of characters. Its
length is the number of characters, it iterates character by
character (Section 2.8), and char_at and chars
address it by character position.
The encoding of a string is exposed only through
to_bytes, which yields the UTF-8 bytes of the
string as an array of Byte values, each in the range 0 to 255, on every
platform. Its inverse is the constructor from_bytes: create String.from_bytes(a)
decodes the Array[Byte] a as UTF-8 and yields a String. It
raises if a is not well-formed UTF-8, rather than substituting a replacement
character, so String.from_bytes(s.to_bytes()) is exactly the string s. The
internal representation by which a host stores a string, and the relation
between a character index and any underlying code-unit index, are
host-defined and not observable except through to_bytes.
A double-quoted string literal interprets the standard backslash escapes
(Section 2.2) — so "\n" is a newline and
\u{h} a code point — while a single-quoted literal
is raw. A control character may also be written with its character
constant or obtained from the standard environment.
| Type | Guaranteed by the Definition | Host-defined |
|---|---|---|
Integer | signed 64-bit two’s-complement, range −263 to 263 − 1, on every platform; checked—overflow and division by zero raise; bitwise on low 32 bits | — |
Real | IEEE 754 double, in representation and arithmetic; division by zero yields an infinity or NaN per IEEE | — |
Char | a Unicode code point | internal representation |
Byte | unsigned 8-bit, 0 to 255; to_byte raises outside the range; arithmetic yields Integer; bitwise on 8 bits | — |
Integer16 | signed 16-bit two’s-complement, −215 to 215 − 1; to_integer16 raises outside the range; arithmetic yields Integer; bitwise on 16 bits | — |
Integer32 | signed 32-bit two’s-complement, −231 to 231 − 1; to_integer32 raises outside the range; arithmetic yields Integer; bitwise on 32 bits | — |
String | sequence of characters; length in characters; to_bytes is UTF-8 as Bytes; from_bytes its strict inverse | internal encoding; character-to-code-unit mapping |
B.4Collection Classes
Three generic collection classes are in the standard environment, each a
Cursor source so that across may traverse it.
Array[T]
An ordered, growable sequence. The display [e₁, …, eₙ] is a
derived form constructing an Array (Appendix C). Representative
methods: get(i), which returns a T; add(x),
which appends; and length, which returns an Integer.
Map[K, V]
An association of keys to values. The display
{k₁: v₁, …} is a derived form; {} is the empty map.
Representative methods: get(k), which returns a V;
put(k, v); and iteration, which yields [key, value] pairs.
Set[T]
An unordered collection of distinct values. The display
#{e₁, …} is a derived form; #{} is the empty set,
which must be written with the explicit set-display syntax to distinguish
it from the empty map.
The constructor from_array builds a set from an array,
discarding duplicates. The methods union,
intersection, difference, contains,
and size provide the usual set algebra.
Byte_Array (a library class)
Byte_Array, loaded with intern data/Byte_Array, is not a
class of the standard environment: it is a shipped library class, described in the reference rather than in this
Definition. It is a fixed-size, mutable sequence of Bytes that a host may keep packed,
one byte per element, and it is a Cursor source whose items are Bytes, so
across may traverse it. Unlike Array[Byte] it has no add; its
slice, concat, and copy yield new values that share no storage
with the original.
B.5Concurrency Classes
The classes underlying Chapter 6 are part of the standard environment.
| Class | Method | Meaning |
|---|---|---|
Task[T] | await | block until done; yield result of type T |
await(ms) | timed await; nil on timeout | |
is_done, is_cancelled | completion and cancellation state | |
cancel | request cancellation | |
await_any, await_all | (free functions, await_all(ts)) wait on an array of tasks | |
Channel[T] | send(v), receive | blocking communication (Section 6.2) |
try_send, try_receive | non-blocking variants used by select | |
with_capacity(n) | (constructor) a buffered channel | |
close, is_closed, size, capacity | channel state |
A spawn whose block assigns result of type T
yields a Task[T]; one that does not yields a plain
Task.
B.6Built-in Values and Effects
A handful of values are bound at the top level of the standard environment. Chief
among them is print, which renders its argument (via
to_string) and writes it to the standard output — a host
effect, and the principal observable behaviour of many programs.
The Console class provides finer output control, including
new_line. A line break in output may be written as the
\n escape in a double-quoted string (Section 2.2), or
produced with the new_line method of a console object
(create Console) or the character constant
#newline.
The Process class provides a further host effect.
create Process (or create Process.self) is a
handle to the running program itself, exposing its environment
(getenv, setenv) and launch arguments
(command_line). create Process.command(…)
instead builds a child operating-system process, configured before
start and afterward controlled and communicated with through
its own environment, standard streams, and lifecycle (wait,
terminate).
The exceptions the language raises itself — on a
nil dereference, a failed contract, a failed runtime argument
check, an out-of-range collection access — are instances of the
classes of §B.7. They are the values a rescue block receives in
exception (Section 5.7).
B.7Built-in Exceptions
When the language itself raises an exception, the value a
rescue block receives in exception is an object of
one of the classes below, chosen by the kind of failure. A value raised with
raise is not wrapped: raise "transient" still hands
the handler the string "transient"
(Section 5.7).
Every class inherits Exception, whose message is
the text the failure is reported with, and whose to_string is that
message, so printing an exception or joining it to a string reads as the report
does.
| Class | Parent | Raised by |
Exception | Any | — the root; message: String |
Contract_Violation | Exception | — a failed assertion; label: ?String |
Precondition_Violation | Contract_Violation | a require clause |
Postcondition_Violation | Contract_Violation | an ensure clause |
Invariant_Violation | Contract_Violation | a class invariant |
Loop_Invariant_Violation | Contract_Violation | a loop invariant |
Variant_Violation | Contract_Violation | a loop variant that is negative or does not decrease |
Assertion_Violation | Contract_Violation | an assert |
Refinement_Violation | Contract_Violation | a refinement predicate at a narrowing site (§5.6) |
Division_by_Zero | Exception | integer / or % by zero |
Arithmetic_Overflow | Exception | an Integer result outside the 64-bit range; abs of a fixed-width minimum |
Void_Access | Exception | a call or field access on nil |
Index_Out_Of_Bounds | Exception | a collection or string position outside its range |
Conversion_Error | Exception | a narrowing conversion out of range, a string that is not a number, bytes that are not UTF-8 |
No_Matching_Clause | Exception | a match with no else that no clause matches |
Channel_Closed | Exception | a send on, or a receive from a drained, closed channel (§6.2) |
Host_Exception | Exception | any other failure the host platform raises; host_class: String names it |
A contract violation’s label is the failed
assertion’s label: pos for require pos: x > 0.
It is nil for a bare assert, whose message names its
source line instead. For a refinement it is the refinement’s name, or the
field’s when the violation is at a field assignment.
do
account.withdraw(500.0)
rescue
if convert exception to v: Precondition_Violation then
print("refused: " + v.message) -- refused: Precondition violation: enough
else
raise exception
end
end
A program may inherit Exception for its own failures, as in
class Parse_Error inherit Exception, delegating to its constructor
make(message), and raise them with raise; they are
then caught and told apart in the same way. raise itself still
accepts any value.