Zebra Syntax Cheat Sheet
One-page reference for Zebra syntax. Print this page!
Variables & Types
var x = 42 # int (inferred)
var s = "hello" # str
var f = 3.14 # float
var b = true # bool
var c = 'x' # char
var x: int = 42 # explicit type
var y: int? = nil # nullable type
Functions
def add(a: int, b: int): int
return a + b
var result = add(2, 3) # 5
def no_return()
print("Just print")
Classes
class Person
var name: str = ""
var age: int = 0
cue init(name: str, age: int)
this.name = name
this.age = age
def describe(): str
return "${name} is ${age}"
var person = Person("Alice", 30)
Cues (methods the compiler calls)
Nine, and only nine: init, toString, equals, hash, compare,
iter, next, begin, end. Anything else is a def. There is no
cue deinit; use using (and cue begin / cue end) for a release
step. Writing a cue as def (def toString) is refused. Full story:
Chapter 07, "Cues".
class Money
var cents: int
cue init(cents: int) # Money(5)
.cents = cents
cue toString(): str # print(m), "${m}"
return "${.cents}c"
cue equals(other: Money): bool # ==, !=
return .cents == other.cents
cue hash(): int # HashMap key / Set element (needs equals)
return .cents
cue compare(other: Money): int # < <= > >=, list.sort() (needs equals)
return .cents - other.cents
class Count3
var n: int = 3
cue next(): int? # for x in obj; nil = done
if .n == 0: return nil
.n = .n - 1
return .n + 1
# cue iter(): I -- return an object whose type has `cue next` (fresh per loop)
class Section
var name: str
cue init(name: str)
.name = name
cue begin() # using obj: on entry
print("<${.name}>")
cue end() # using obj: on exit
print("</${.name}>")
def evens(limit: int): Iter(int) # generator: the compiler writes `cue next`
var i = 0
while i < limit
yield i
i = i + 2
def main()
print(Money(5) < Money(7)) # true
for x in Count3()
print(x) # 3 2 1
for e in evens(5)
print(e) # 0 2 4
using Section("p")
print("hi") # <p> hi </p>
Without equals, == on a class is identity. Without compare, < and
sort() on a class do not build. Structs take the same cues, by value;
@derive(Debug, Eq, Hash) writes the first three.
Interfaces
interface Shape
def area(): float
def perimeter(): float
class Circle implements Shape
var radius: float = 0.0
def area(): float
return 3.14159 * radius * radius
def perimeter(): float
return 2.0 * 3.14159 * radius
Structs & Unions
struct Point
var x: int
var y: int
union Value
int_: int
str_: str
none_
# Pattern matching
branch v
on Value.int_ as n
print(n)
on Value.str_ as s
print(s)
else
pass
Composition & Mixins
Zebra has no class inheritance. Reuse via interfaces (Ch 08), mixins
(adds), or composition (fields).
# Mixin — shared methods pulled into multiple classes:
mixin Loggable
def log(message: str)
print("[log] ${message}")
class UserService adds Loggable
cue init()
pass
# `log()` is now a method on UserService.
# Composition — has-a, not is-a:
class Storage
var path: str = ""
class Report
var storage: Storage # field-held helper
cue init(p: str)
storage = Storage()
storage.path = p
See Chapter 09 for the full picture.
Control Flow
# If/Else if/Else
if x > 10
print("Large")
else if x > 5
print("Medium")
else
print("Small")
# While loop
var i = 0
while i < 10
print(i)
i = i + 1
# For loop
for item in items
print(item)
for i in 0..10
print(i) # 0, 1, 2, ..., 9
# Break & Continue
while true
if x == 5
break
if x == 2
x = x + 1
continue
print(x)
x = x + 1
Operators
# Arithmetic (integer overflow stops the program, in every build)
10 + 5, 10 - 5, 10 * 5, 10 / 5, 10 % 3
# Wrapping / saturating integer arithmetic
b +% 10, b -% 10, b *% 2, -%b # wrap around the type's range
b +| 10, b -| 10, b *| 2, b <<| 1 # clamp at the type's bounds
# Typed literals
300_i16, 5_u8, 7u, 0xFF_u8, 1.5_f32
# Comparison
10 > 5, 10 < 5, 10 >= 5, 10 <= 5
10 == 5, 10 != 5
# Logical
true and false
true or false
not true
# String
"hello" + " world"
"Value: ${x}" # interpolation
Collections
# List
var numbers = List(int)()
numbers.add(1)
numbers.add(2)
for num in numbers
print(num)
numbers.count()
numbers.at(0)
numbers.contains(1)
numbers.remove(1)
# HashMap
var map = HashMap(str, int)()
map.put("a", 1)
map.fetch("a") # retrieve value
map.contains("a")
map.remove("a")
for key, value in map
print("${key}: ${value}")
Strings
var text = "Hello, World!"
# Properties & Methods
text.len # length
text.upper() # uppercase
text.lower() # lowercase
text.contains("World") # substring search
text.startsWith("Hello") # prefix check
text.endsWith("!") # suffix check
text.split(",") # split to List(str)
text.replace("World", "Zebra") # replace all occurrences
text.trim() # remove whitespace
text.indexOf("World") # find position (-1 if not found)
text.reverse() # reverse string
text.repeat(3) # repeat N times
# Type conversion
"42".toInt() # int (0 on bad input; "42".tryInt() is int?)
"3.14".toFloat() # float (0.0 on bad input; tryFloat() is float?)
42.toString() # "42"
Error Handling
# Functions that can fail use throws
def parse(text: str): int throws
if text.len == 0
raise "Empty input"
return 42
# Catch expression (inline fallback)
var value = parse("abc") catch 0
# Method-level catch (there is no try/catch block; a catch clause
# attaches to a def, at the same indent as def)
def attempt()
var v = parse("")
print(v)
catch |err|
print("Error: ${err.message}")
# Catch expression fallback — no binding needed for a fixed value
var result = parse("x") catch -1
Nil Safety
var x: int? = 42 # Can be int or nil
var y: int? = nil # Explicitly nil
# Safe check
if x != nil
var safe_x = x + 1 # Safe now
Generics
# Generics are class-scoped in Zebra — there is no free top-level generic
# function; a generic method lives on a generic class:
class Box(T)
var item: T?
def store(value: T)
this.item = value
def retrieve(): T?
return this.item
var box = Box(str)()
box.store("hello")
Pipelines
var result = text -> .trim() -> .upper() -> .split(" ")
# Equivalent to:
var trimmed = text.trim()
var upper = trimmed.upper()
var parts = upper.split(" ")
File I/O
# Read file (File.read/write are not `throws`; guard with File.exists)
var content = File.read("file.txt")
# Write file
File.write("output.txt", content)
# Check existence
if File.exists("file.txt")
print("File exists")
Regular Expressions
var pattern = Regex.compile("[a-z0-9]+@[a-z]+\\.[a-z]+")
pattern.match("user@example.com") # bool (the WHOLE string must match)
pattern.find(text) # str (first match, "" if none)
pattern.findAll(text) # List(str) (all matches)
pattern.replace(text, "X") # str (replaces every match)
pattern.split(text) # List(str) (split by match)
System Access
sys.args() # List(str): command-line args
sys.exit(code) # Exit program
Arg.parse() # Structured argument parsing
Modules
use MathUtils # Import module
use ast exposing Stmt, Expr, TypeRef # Selective import
Math Module
Math.PI # 3.14159...
Math.E # 2.71828...
Math.sin(x) # Trig functions
Math.sqrt(x) # Square root
Math.pow(x, y) # Power
Math.abs(x) # Absolute value
Math.min(a, b) # Minimum
Math.max(a, b) # Maximum
Math.floor(x) # Round down
Math.ceil(x) # Round up
Static Methods
class MathUtils
static
def add(a: int, b: int): int
return a + b
MathUtils.add(2, 3) # Call without instance
The keyword is static (the older shared was renamed in 2026-04-19).
Contracts (require / ensure / invariant)
class Counter
var count: int = 0
invariant
count >= 0
def increment(): int
ensure
result == old count + 1 # `result` = return value; `old expr` = pre-call snapshot
count = count + 1
return count
def decrement_by(n: int)
require
n > 0
n <= count
count = count - n
Pass --turbo to strip all contract checks. It is independent of --release
(which optimises); a shipping build uses both: zebra --release --turbo app.zbr.
See Chapter 14.
Reflection — Reflect.* / @reflectable
# Tier 1: static field/type-name arrays:
class User
var name: str = ""
var age: int = 0
print(Reflect.className(u)) # "User"
for n in Reflect.fieldNames(u)
print(n)
# Tier 3: strict JSON deserialization
@reflectable
class User
var name: str = ""
var age: int = 0
if Json.parseStrict(User, src) as u
print(u.name)
Strict semantics: missing key, type mismatch, or extra key → nil. Scope-1 fields: int / float / bool / str.
Comments
# Single-line comment
# Multi-line comments
# are just multiple
# single-line comments
Keywords Quick Reference
| Keyword | Use |
|---|---|
var | Declare variable |
def | Define function |
class | Define class |
struct | Define value type |
union | Define tagged union |
enum | Define enumeration |
interface | Define interface |
is | Member attribute / type check |
implements | Implement interface |
if, else if, else | Conditional |
while | While loop |
for | For loop |
break | Exit loop |
continue | Skip iteration |
branch | Pattern matching |
on | Pattern case |
return | Return from function |
raise | Signal an error |
throws | Mark function as fallible |
catch | Error handling (expr catch fallback, method-level catch) |
except | Struct update copy |
nil | Null value |
true, false | Boolean literals |
as | Binding clause (if x as n, branch on V as r) |
static | Type-associated (class-level) member |
mixin | Declare a reusable bag of methods |
adds | Include a mixin into a class |
require | Precondition contract |
ensure | Postcondition contract |
invariant | Class invariant contract |
result | Return-value reference inside ensure |
old | Pre-call snapshot inside ensure |
assert | Inline sanity check |
allocate | Bounded-scope memory block (allocate Arena()) |
cue | Method the compiler calls for you (cue init, cue toString, cue equals, ...) |
yield | Produce the next value in a generator (a def returning Iter(T)) |
capture | Per-closure persistent state, at the top of a lambda body |
sig | Named function type (sig Predicate(item: str): bool) |
using | Block scoped by a resource with cue begin() / cue end() (using g.vbox(...) in GUIs) |
with | Contextual receiver (with obj → bare x = 5 means obj.x = 5) |
orelse | Nil fallback (maybe orelse 0) |
to | Only as a method name, e.g. for i in 0.to(10); there is no x to T conversion operator — use .toInt(), .toFloat(), .toString() |
this | Current object reference |
use | Import module |
@reflectable | Opt class into Tier-3 reflection (Json.parseStrict) |
exposing | Selective import |
where | Type constraint |
Quick Type Reference
| Type | Example | Default |
|---|---|---|
int | 42 | 0 |
float | 3.14 | 0.0 |
bool | true | false |
str | "hello" | "" |
char | 'x' | '' |
T? | 42 or nil | nil |
List(T) | List(int)() | empty |
HashMap(K,V) | HashMap(str, int)() | empty |
^T | ^Node | heap pointer |
Operator Precedence
()— function call, array accessnot— logical NOT*,/,%— multiplication, division, modulo+,-— addition, subtraction<,>,<=,>=,==,!=— comparisonand— logical ANDor— logical OR=— assignment
Print this page for quick reference!
For more details, see the full book or use the Troubleshooting guide (Appendix C).