Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Zebra: 30 Minutes to Productive

Get from zero to your first working Zebra program in 30 minutes.


Minute 1-3: What is Zebra?

Zebra is a modern programming language combining:

  • Python's simplicity — readable, clear syntax
  • C#'s power — strong types, interfaces, generics
  • Nil safety — prevents null pointer crashes
  • Error handling — explicit throws/raise/catch instead of hidden exceptions
  • Performance — compiles to Zig → machine code

You can read your first working program in 2 minutes.


Before You Start: Installing Zebra

One command installs a release — the zebra compiler plus the Zig toolchain it builds with:

# Windows (PowerShell)
irm https://raw.githubusercontent.com/torial/zebra-language/main/install/install.ps1 | iex

# Linux and macOS
curl -fsSL https://raw.githubusercontent.com/torial/zebra-language/main/install/install.sh | sh

On Windows the installer adds Zebra to your PATH; on Linux and macOS it prints the export PATH=... line to add to your shell profile. Open a new terminal and check with zebra --version. Windows, Linux and macOS (Apple Silicon, first-class since 0.9.0-rc2) are all supported, and zebra up later updates an installed release.

Prefer to build from source? With Zig 0.16 on your PATH:

git clone https://github.com/torial/zebra-language
cd zebra-language
zig build                          # produces zig-out/bin/zebra (zebra.exe on Windows)

Minute 4-10: Your First Program

Create a file: hello.zbr

def main()
    print("Hello, World!")

Compile and run:

zebra hello.zbr

That's it! You just wrote your first Zebra program.


Minute 11-20: The Five Things You Need to Know

1. Variables and Types

var name = "Alice"              # str
var age = 30                    # int
var pi = 3.14                   # float
var active = true               # bool

Key: Zebra figures out the type. You don't need to write it.

2. Functions

def greet(name: str): str
    return "Hello, ${name}!"

var message = greet("Bob")      # "Hello, Bob!"

Key: Parameters and return types are explicit (safer).

3. Collections

var fruits = List(str)()        # Create list
fruits.add("apple")
fruits.add("banana")

for fruit in fruits
    print(fruit)

Key: Lists are for ordered collections. Use for...in to loop.

4. If/Else and Loops

var x = 10

if x > 5
    print("Large")
else
    print("Small")

while x > 0
    print(x)
    x = x - 1

Key: No parentheses needed. Indentation matters.

5. Error Handling

# A function that can fail is marked `throws`; `raise` signals the failure
def parse_age(text: str): int throws
    var n = text.tryInt()
    if n == nil
        raise "not a number: ${text}"
    return n!

# Or attach a catch clause to a def for more control (there is no
# standalone `try` block — `catch` always attaches to a def's body)
def show_age(text: str)
    var age = parse_age(text)
    print("Age: ${age}")
catch |err|
    print("Error: ${err}")

def main()
    print(parse_age("42") catch -1)       # 42
    print(parse_age("forty") catch -1)    # -1   (the inline fallback)
    show_age("42")                        # Age: 42
    show_age("forty")                     # Error: not a number: forty

Key: Functions annotated with throws can fail. Use inline expr catch fallback for a default value, or a method-level catch clause (attached to a def, after its body) for structured handling.


Minute 21-25: Build Something Real

Create count_lines.zbr:

def main()
    var filename = "input.txt"

    # File.read is not `throws` (a missing file stops the program), so check first
    if not File.exists(filename)
        print("Error: could not find ${filename}")
        sys.exit(1)

    var content = File.read(filename)
    var lines = content.split("\n")

    print("File: ${filename}")
    print("Lines: ${lines.count()}")
    print("Total characters: ${content.len}")

Run:

zebra count_lines.zbr

You now have a working file analyzer!


Minute 26-30: The Pattern to Remember

99% of Zebra code follows this pattern:

def do_something(input: str): str throws
    # 1. Validate input
    if input.len == 0
        raise "Input is empty"

    # 2. Do work
    var result = input.trim().upper()

    # 3. Return success
    return result

def try_it()
    var v = do_something("")?
    print(v)
catch |err|
    print("Failed: ${err}")

def main()
    # Use catch for simple fallback
    var value = do_something("data") catch "default"
    print(value)                    # DATA

    # Or a method-level catch clause for error details
    try_it()                        # Failed: Input is empty

This pattern handles errors explicitly. No surprises, no crashes.


Next Steps (5 More Minutes)

  • Read Chapter 01 (Getting Started) — 15 minute deep dive
  • Try examples — Every chapter has copy-paste examples
  • Build projects — Chapters 16-18 show complete projects
  • Reference cheat sheet — See CHEATSHEET-Syntax.md for all syntax

Troubleshooting Your First Program

"error: file not found" -> Make sure your .zbr file exists and the compiler can find it

"error: expected str, got int" -> Use .toString() or string interpolation: "${number}"

Program compiles but does nothing -> Add a top-level def main(), or a class Main with static def main — one of those is your entry point


Cheat Sheet: Five Commands

var x = 5                       # Declare variable
def func(): str               # Define function
for item in list                # Loop over collection
if x > 5                        # Conditional
var v = risky() catch default   # Handle errors

You're Ready!

You now understand:

  • Variables and types
  • Functions
  • Collections
  • Control flow
  • Error handling

Read Chapter 02 next to understand the type system deeper, or jump straight to Chapter 16 to build a real project.

Welcome to Zebra!