Tzopilotl
Docs
GitHub

Tzopilotl by Example

3. Types and the Numeric Tower

3.1 Built-in Types

Int       -- 64-bit signed integer
Float     -- 64-bit double precision float
Bool      -- boolean (stored as 64-bit integer)
String    -- UTF-8 string
Symbol    -- interned string, compared by pointer
Fraction  -- rational number (64-bit numerator/denominator)
Complex   -- complex number (two 64-bit floats)
Void      -- no value

3.2 Numeric Tower

Types promote automatically: IntFractionFloatComplex

-- Fraction literals (no spaces around /)
let half = 1/2;           -- 1/2 (fraction)
let third = 1/3;          -- 1/3 (fraction)
let sum = half + third;   -- 5/6 (fraction)

-- Int division also produces a fraction
let q = 7 / 4;           -- 7/4 (fraction)

-- Int + float promotes to float
let x = 2 + 3.5;         -- 5.5 (float)

-- Imaginary literals create complex numbers
let z = 3.0 + 4i;        -- (3+4i) (complex)
let w = Complex(1.0, -1.0); -- (1-1i)

3.3 Collection Types

-- Array type (homogeneous)
[Int]                 -- array of integers
[Float]               -- array of floats
[String]              -- array of strings

-- Array literals
let nums = [1, 2, 3, 4, 5];
let floats = [1.0, 2.0, 3.0];

-- Tuple literals (heterogeneous, fixed-length)
let point = (3, 4);
let mixed = (1, 2.5, "hello");

-- 1-element tuple (trailing comma distinguishes from parenthesized expression)
let single = (42,);     -- type: (Int,)
single println;          -- (42,)
single.0 println;        -- 42

-- Empty tuple (unit value)
let u = ();              -- type: ()
u println;               -- ()

-- Trailing commas are allowed in all tuples
let t3 = (1, 2, 3,);   -- same as (1, 2, 3)

-- Map literals (key: value pairs)
let ages = ["Alice": 30, "Bob": 25];

-- Set construction
let unique = Set(1, 2, 3);

-- Persistent (immutable) collections: a leading # on the type and literal
let pv #[Int] = #[1, 2, 3];        -- persistent vector
let pm #[String:Int] = #["a": 1];   -- persistent map

-- Access by index
let first = nums[0];     -- array index
let x = point.0;        -- tuple field access

-- Array indexing is cyclic (wraps around)
let a = [10, 20, 30];
a[0]                     -- 10
a[3]                     -- 10 (wraps around)
a[-1]                    -- 30 (last element)
a[-2]                    -- 20

-- Indexed assignment: writes also wrap cyclically
var b = [10, 20, 30];
b[0] = 100;
b[-1] = 999;
b println;               -- [100, 20, 999]

-- Mutating builtins: push! / pop! / clear! modify the array in place
b push!(42);
b println;               -- [100, 20, 999, 42]
let last = b pop!;
last println;            -- 42
b clear!;
b isEmpty println;       -- true (isEmpty works on every collection)

-- Non-mutating push / pop return a new array (input unchanged)
let orig = [1, 2, 3];
let extended = orig push(99);
orig println;            -- [1, 2, 3]
extended println;        -- [1, 2, 3, 99]

-- Arrays are passed by reference; copy when an alias must be independent
var c = [1, 2, 3];
var d = c copy;
c push!(4);
c println;               -- [1, 2, 3, 4]
d println;               -- [1, 2, 3]

Mutating-function names: the trailing !

Names ending in ! (push!, pop!, insert!, …) mutate their first argument in place; names without ! return new values and leave their inputs untouched. The ! is part of the identifier — foo and foo! are different functions. The convention is idiomatic but not enforced; a user-defined mutating function can be named with or without ! as you choose.

Mutable vs. persistent collections

Array, Map, and Set are mutable heap objects passed by reference. The #[…] forms are persistent (immutable) collections — a persistent vector and a persistent map — that never change in place: every update returns a new value sharing structure with the old one. They are distinct types with no implicit conversion to or from the mutable kinds. See Persistent Collections.