Semantics

This page settles four things the rest of the documentation relies on: what a number is, what null means, how values compare, and what happens when something goes wrong.

Two ways a program fails

An operation that cannot produce an answer either yields null or disrupts, and one rule decides which:

An operation yields null when its result type has a defined absence, and disrupts when it does not.

Numbers have such an absence — null is exactly “no number.” Text and logical do not: there is no text that isn’t there, and no logical that isn’t there. So arithmetic can fail by returning something, and the rest must fail by stopping.

Arithmetic yields null. Every arithmetic operator — +, -, *, /, //, %, and unary negation — produces a number or null, and never disrupts. You get null when the calculation has no answer, and equally when an operand was not a number in the first place. null is not a number, so it absorbs too.

var a = 1 / 0        // null — no answer
var b = 2 + "x"      // null — "x" is not a number
var c = null + 1     // null — null is not a number

null then propagates: arithmetic on null yields null, so a bad value early in a calculation surfaces as null at the end rather than as a wrong number.

Everything else disrupts. When the operation has no way to produce a value at all, the actor disrupts:

var t = "a" & {}     // disrupt — a record has no text form
var u = null.field   // disrupt — reading through null
var v = null()       // disrupt — calling null
stone(rec).x = 1     // disrupt — writing a stone value
1 < "a"              // disrupt — not two numbers or two texts

Handle a disruption with a disruption block:

var parse = function(text) {
  var value = number(text)
  return value
  disruption {
    return null
  }
}

Numbers

Every build has a largest whole number it represents exactly. A number within that range is a fit number, and is_fit tests it:

is_fit(1000)     // true
is_fit(1.5)      // false — not a whole number

The range is chosen per target: a desktop build, a handheld build, and a decimal build each set it differently. Write against is_fit rather than a constant and the same source behaves correctly everywhere.

Past that range, whole numbers may round. Arithmetic with no representable answer yields null.

The fit module operates on the same target-selected width.

Null

null is the absence of a value. It is what you get from a missing record field, an index past the end of an array, a numeric malady, and a function that returns nothing.

var r = {}
r.missing            // null
var a = [1, 2, 3]
a[99]                // null

Reading null is fine. Reaching through it — as a record, or as a function — disrupts.

Comparison

Equality works across every pair of values. Two values of different types are simply not equal:

1 == 1               // true
1 == "1"             // false
{} == {}             // false — different records

Ordering is narrower. <, >, <=, and >= compare two numbers or two texts:

1 < 2                // true
"a" < "b"            // true

Any other operand pairing disrupts — a number against a text, a record against a record, anything that is not two numbers or two texts:

1 < "a"              // disrupt
{} < {}              // disrupt

Text orders by character sequence.

Text and concatenation

+ adds numbers. & joins text. Neither does the other’s job.

1 + 2                // 3
"n = " & 42          // "n = 42"
"ab" & "cd"          // "abcd"

& converts a number operand to text first. If either operand is still not text after that — a logical, a record, an array, a blob, a function, or null — it disrupts, because there is no text to produce and no empty answer worth inventing.

"n = " & true        // disrupt
"n = " & null        // disrupt

That last one matters in combination with arithmetic. Since a failed calculation yields null, and & disrupts on null, a bad number surfaces at the point you try to render it:

var label = "total: " & (price + quantity)   // disrupts if either was not a number

Why two operators

Neither + nor & is overloaded, and that is the point. Writing + is a claim that both operands are numbers; writing & is a claim that the result is text. Since ƿit has no declared types, the operator you choose is where that intent gets recorded — at the site, by you, as a side effect of saying what you meant. It tells a reader what the expression is for, and it tells the compiler what shape to expect.