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
nullwhen 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.