02 · Primitives & Numbers
Numeric Types
Fixed-width aliases (u8, i32, f32, usize) as annotations and suffixes.
On this page
For APIs that need explicit bit widths — network ports, GPU buffers, C interop —
Zolo accepts sub-64 aliases (u8, i32, f32, usize…) both as type
annotations and as literal suffixes (42u8, 1.5f32). In the current MVP they
all map to int/float; overflow-checked types are planned:
Fixed-width aliases: as annotations and as literal suffixes.
16-numeric-types.zolo
// Feature: concrete numeric types and literal suffixes
// When to use: write APIs whose intent is clearer with explicit widths
// (`u16` for a port number, `u8` for a byte) or whose values must
// round-trip through FFI / GPU buffers (`f32`, `u32`).
//
// These are real, distinct concrete types. Suffixed literals are checked at
// build time, retain their width in every backend, and surface that exact type
// in editor hover/inlay hints. Short suffixes canonicalize semantically:
// `i` = i32, `u` = u32, and `f` = f32.
//
// See `specs/numeric-types.html`.
// 1. Type annotations: sub-64-bit aliases ----------------------------
let port: u16 = 8080
let pixel: u8 = 255
let coord: f32 = 1.5
let offset: i32 = -100
let big: u64 = 1_000_000_000
print("port={port}, pixel={pixel}, coord={coord}, offset={offset}, big={big}")
// 2. Literal suffixes -----------------------------------------------
let f32_val = 1.5f32 // explicit f32
let f_short = 1.5f // short form (= f32)
let i32_val = 42i32 // explicit i32
let i_short = 42i // short form (= i32)
let u_short = 42u // short form (= u32)
let port_lit: u16 = 8080u16
print("f32={f32_val}, f={f_short}, i32={i32_val}, i={i_short}, u={u_short}, port={port_lit}")
// 3. Hex / binary / octal with suffixes -----------------------------
let mask: u32 = 0xFF_FF_00_00u32
let bits: u8 = 0b1010_0001u8
let perms: u16 = 0o644u16
print("mask={mask}, bits={bits}, perms={perms}")
// 4. Underscores for readability ------------------------------------
let count: u64 = 1_000_000_000_000u64
let chunked = 0xFF_FF_FF_FFu32
print("count={count}, chunked={chunked}")
// Current guarantees:
// - `300u8` is rejected at build time because it does not fit.
// - Distinct concrete types require an explicit conversion (`i32 as i64`).
// - Unsuffixed literals remain ergonomic and adapt to their expected type,
// as in `let pixel: u8 = 255` above.
Challenge
The example declares let pixel: u8 = 255 using a type annotation. Rewrite it
to use a literal suffix instead (255u8). Does the output change? What happens
if you try 256u8?
See also