Every value in SCUA is one of a handful of kinds. This page names them, shows how to ask a value what it is, and covers the two things newcomers trip on most: integers and floats are separate, and only two values count as false.
You write SCUA without type annotations and it behaves like a dynamic language. The kinds below are what values are at runtime, whether or not you annotate anything. Annotations add compile-time checks on top; see Records and gradual types.
#The kinds
There are eight value kinds you'll meet right away. The
type() builtin returns the kind of a value as a string:
print(type(nil))
print(type(true))
print(type(42))
print(type(3.14))
print(type("hi"))
print(type([1, 2, 3]))
print(type({ x = 1 }))
print(type(fn(n) return n end))
Run it:
$ scua kinds.scua
nil
bool
int
float
string
array
table
fn
So the kinds are nil, bool,
int, float, string,
array, table, and fn. A few
notes:
- nil is the absence of a value. A missing table
field reads as
nil, and a function with noreturngives backnil. - bool is
trueorfalse. - int and float are both 64-bit numbers, and they are not the same kind. More on that below.
- string is text. See Strings and interpolation.
- array is an ordered, 0-based sequence written with square brackets. table is a string-keyed map written with braces. Both are covered in Collections.
- fn is a function value. See Functions and closures.
There are a few more kinds you'll meet later:
decimal for exact base-10 numbers like money and
big for huge-magnitude idle-game numbers (both on the
Numbers page), vectors, matrices, and
quaternions for math (see Math), and the
Ok/Error values that represent results of
operations that can fail (see Errors and
faults).
#Numbers in depth
int and float are different kinds and never
mix silently — / always gives a float, //
stays an int — and SCUA adds two specialist number kinds:
decimal (exact base-10, for money) and
big (unbounded magnitude, for idle games).
How each is stored, the rules for mixing them, and when to use which are
their own page: Numbers.
#Truthiness
In a condition, only nil and false are
false. Everything else is true, including 0, the empty
string, and an empty array.
fn check(v)
if v then return "truthy" else return "falsy" end
end
print(check(nil))
print(check(false))
print(check(0))
print(check(""))
print(check([]))
Run it:
$ scua truthy.scua
falsy
falsy
truthy
truthy
truthy
If you're used to languages where 0 or an empty
collection is false, watch this one. To test for an empty array, compare
its length: if len(xs) == 0 then ....
#Equality
== compares values, and != is its negation.
Numbers compare by value across int and float, so 1 == 1.0
is true. Strings compare by their contents.
print(1 == 1.0)
print("ed" == "ed")
print("ed" == "Ed")
print(1 == "1")
print([1, 2] == [1, 2])
let a = [1, 2]
let b = a
print(a == b)
print(3 != 4)
Run it:
$ scua equality.scua
true
true
false
false
true
true
true
Two things to know:
- Values of different kinds are never equal.
1 == "1"is false; the int and the string are not compared after any conversion. (Akeyis its own kind too, so$"hp" == "hp"is false.) - Arrays, tables, and records compare by value —
their contents, compared element by element (and recursively for nested
ones). Two separately built arrays with the same elements are
equal:
[1, 2] == [1, 2]istrue, and{ x = 1 } == { x = 1 }istrue. Two names for the same value are equal too. (Comparing two different kinds, or two records with different fields, isfalse.)
#Asking whether two values are the same object
Because == compares contents, it can't tell you whether
two names refer to one object or to two that happen to match.
is(a, b) answers that:
let a = { x = 1 }
let b = { x = 1 }
let c = a
print(a == b)
print(is(a, b))
print(is(a, c))
$ scua identity.scua
true
false
true
Use it for cycle detection, identity maps, and "have I already seen this one?" checks.
For values that have no identity to speak of — numbers, strings,
bytes, keys, and the vector and colour types —
there is nothing to tell two equal ones apart, so is
compares the value: is("hi", "h" .. "i") is
true. It stays stricter than == where the two
values really are distinguishable, though: 1 and
1.0 are == but not is (different
types), and so are 1.0d and 1.00d (they print
differently).
#Values that contain themselves
A structure can point back at itself, and == handles
that without hanging — but the answer is worth knowing before you rely
on it. A value always equals itself, however deep or
cyclic it is. Two separately built structures that both contain
themselves compare false, even when they look
identical:
let a = {}
a.self = a
let b = {}
b.self = b
print(a == a)
print(a == b)
$ scua cycles.scua
true
false
That is a deliberate choice rather than an accident: comparing two
cycles element by element never terminates, so the comparison gives up
and answers false rather than looping. a == a
still works, which is what a cycle-safe printer or a "have I already
visited this?" check actually needs.