Zamin Docs

Language Guide

A complete reference for the Zamin programming language. This guide covers every core feature — from variables and types to structs, pattern matching, and modules. Each section includes explanations and runnable code examples.

Variables and Types

Zamin uses let for mutable variables and const for constants. You never write explicit type annotations — the compiler infers types from the assigned value.

Zamin
let x = 42;
let name = "Zamin";
let pi = 3.14159;
let active = true;

const MAX_SIZE = 1024;
const GREETING = "hello";

Core Types

Zamin provides these built-in types:

  • int — signed 64-bit integer (i64)
  • uint — unsigned 64-bit integer (u64)
  • float — 64-bit floating-point (f64)
  • booltrue or false
  • string — UTF-8 string
  • nil — absence of value
  • list — ordered, growable collection
  • dict — key-value mapping
  • set — unordered collection of unique values
  • tuple — fixed-size, immutable collection

Type Conversion

Use built-in conversion functions to change between types:

Zamin
let n = int("42");       // 42
let f = float("3.14");   // 3.14
let s = string(100);     // "100"
let b = bool(1);         // true
let b2 = bool(0);        // false
💡 Tip

Constants must be assigned a value at declaration time and cannot be reassigned. Use them for configuration values and mathematical constants like const PI = 3.14159;

Strings

Strings in Zamin are UTF-8 encoded. Both double quotes and single quotes create identical strings.

Zamin
let s1 = "hello";
let s2 = 'hello';
// s1 and s2 are equivalent

F-Strings

Prefix a string with f to embed expressions inside {}:

Zamin
let name = "Zamin";
let version = 2;

println(f"Welcome to {name} v{version}!");
// Output: Welcome to Zamin v2!

let x = 10;
println(f"Result: {x * 2 + 1}");
// Output: Result: 21

Concatenation and Repetition

Zamin
let greeting = "Hello" + ", " + "world!";
println(greeting);
// Output: Hello, world!

let pattern = "ab" * 3;
println(pattern);
// Output: ababab
💡 Tip

Use the string module for advanced operations like splitting, joining, trimming, and case conversion. See Standard Library for details.

Numbers

Integer Literals

Zamin
let decimal = 42;
let negative = -7;
let hex = 0xff;
let binary = 0b1010;
let octal = 0o77;

Unsigned Integers

Zamin
let count = 42u;
let size = 1024u;

Floating-Point Literals

Zamin
let pi = 3.14159;
let negative = -0.5;
let huge = 1e10;

Arithmetic Operators

OperatorDescriptionExample
+Addition3 + 25
-Subtraction3 - 21
*Multiplication3 * 26
/Division7 / 23.5
//Integer division7 // 23
%Modulo7 % 21
**Power2 ** 101024
Zamin
println(10 / 3);      // 3.33333...
println(10 // 3);     // 3
println(10 % 3);      // 1
println(2 ** 8);      // 256

Comparison Operators

All standard comparisons work: ==, !=, <, >, <=, >=.

💡 Tip

Use the math module for advanced math functions like math.sqrt(), math.abs(), math.floor(), and math.ceil().

Booleans

Zamin has two boolean literals: true and false.

Logical Operators

Zamin
let a = true;
let b = false;

println(a and b);   // false
println(a or b);    // true
println(not a);     // false

if not b {
    println("b is false");
}

Truthiness

In conditional contexts (like if and while), the following values are considered falsy:

  • false
  • nil
  • 0 and 0.0
  • "" (empty string)
  • [] (empty list)

Everything else is truthy, including non-empty strings, non-zero numbers, and non-empty collections.

Zamin
if "hello" {
    println("truthy!");
}

if 0 {
    println("never reached");
}

if [] {
    println("never reached");
}

Collections

Lists

Lists are ordered, heterogeneous, and growable. Create them with square brackets.

Zamin
let fruits = ["apple", "banana", "cherry"];
let mixed = [1, "two", true, 3.0];

// Indexing (0-based)
println(fruits[0]);   // "apple"
println(fruits[-1]);  // "cherry" (last element)

// Slicing
println(fruits[0:2]); // ["apple", "banana"]

// Modification
fruits.push("date");
fruits.pop();

// Length
println(len(fruits)); // 3

// Membership
if "apple" in fruits {
    println("Found apple!");
}

Dicts

Dicts map keys to values. Use curly-brace syntax and dot-notation for access.

Zamin
let user = {
    name: "Alice",
    age: 30,
    active: true,
};

// Access with dot notation
println(user.name);    // "Alice"

// Access with bracket notation
println(user["age"]);  // 30

// Add / update fields
user.email = "alice@example.com";

// Get with default
let city = user.get("city", "unknown");

// Iterate
for key in user.keys() {
    println(f"{key}: {user[key]}");
}

for value in user.values() {
    println(value);
}

for key, value in user.items() {
    println(f"{key} = {value}");
}

Sets

Sets store unique, unordered values. Use curly-brace syntax with distinct elements.

Zamin
let colors = {"red", "green", "blue"};

colors.add("yellow");
colors.remove("green");

println(len(colors)); // 3

let a = {1, 2, 3};
let b = {2, 3, 4};

println(a | b);   // {1, 2, 3, 4}  — union
println(a & b);   // {2, 3}        — intersection
println(a - b);   // {1}           — difference

if 2 in a {
    println("a contains 2");
}

Tuples

Tuples are fixed-size, immutable collections. Once created, they cannot be changed.

Zamin
let point = (3, 4);
println(point[0]);  // 3
println(point[1]);  // 4

// Destructuring
let (x, y) = point;
println(f"x={x}, y={y}");

// Return multiple values from a function
func minmax(list) {
    return (min(list), max(list));
}

let (lo, hi) = minmax([3, 1, 4, 1, 5]);
println(f"min={lo}, max={hi}");

List Comprehensions

Create lists with concise expressions that iterate, filter, and transform in a single line.

Zamin
let doubled = [x * 2 for x in 0..10];
// [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

let evens = [x for x in 0..20 if x % 2 == 0];
// [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

let words = ["hello", "world"];
let upper = [w.to_upper() for w in words];
// ["HELLO", "WORLD"]

let nums = [1, 2, 3, 4, 5];
let filtered = [x * x for x in nums if x > 2];
// [9, 16, 25]

Range Expressions

Ranges generate sequences of integers. They are commonly used in for loops and comprehensions.

Zamin
// Exclusive end (0 to 9)
for i in 0..10 {
    print(f"{i} ");
}
// Output: 0 1 2 3 4 5 6 7 8 9

// Inclusive end (0 to 10)
for i in 0..=10 {
    print(f"{i} ");
}
// Output: 0 1 2 3 4 5 6 7 8 9 10

let list = [10..=15];
println(list); // [10, 11, 12, 13, 14, 15]
💡 Tip

Use .. when the end value should be excluded (like Python's range()) and ..= when you want the end value included.

Control Flow

If / Elif / Else

Zamin
let score = 85;

if score >= 90 {
    println("A");
} elif score >= 80 {
    println("B");
} elif score >= 70 {
    println("C");
} else {
    println("F");
}
// Output: B

Ternary Expressions

Assign the result of a conditional expression to a variable:

Zamin
let age = 20;
let status = if age >= 18 { "adult" } else { "minor" };
println(status); // "adult"

let x = 5;
let label = if x > 0 { "positive" } else if x < 0 { "negative" } else { "zero" };
println(label); // "positive"

While Loops

Zamin
let count = 0;
while count < 5 {
    println(count);
    count += 1;
}

// Infinite loop with break
let total = 0;
while true {
    let input = read_line();
    if input == "quit" {
        break;
    }
    total += int(input);
}

For Loops

Zamin
// Iterate over a list
let items = ["a", "b", "c"];
for item in items {
    println(item);
}

// Iterate with index
for i, item in items {
    println(f"{i}: {item}");
}

// Iterate over a range
for i in 0..5 {
    println(i);
}

// Iterate with inclusive range
for i in 0..=5 {
    println(i);
}

// Iterate over a dict
let map = { x: 1, y: 2 };
for key, value in map {
    println(f"{key} = {value}");
}

Break and Continue

Zamin
// break exits the innermost loop
for i in 0..100 {
    if i == 5 {
        break;
    }
    println(i);
}
// Output: 0 1 2 3 4

// continue skips to the next iteration
for i in 0..10 {
    if i % 2 == 0 {
        continue;
    }
    println(i);
}
// Output: 1 3 5 7 9

Match (Pattern Matching)

The match statement compares a value against multiple patterns and executes the matching branch. Use _ as the wildcard for a default case.

Zamin
let command = "start";

match command {
    case "start" => println("Starting..."),
    case "stop"  => println("Stopping..."),
    case "pause" => println("Pausing..."),
    case _       => println("Unknown command"),
}

// Matching numbers
let day = 3;

match day {
    case 1 => println("Monday"),
    case 2 => println("Tuesday"),
    case 3 => println("Wednesday"),
    case 4 => println("Thursday"),
    case 5 => println("Friday"),
    case 6 | 7 => println("Weekend"),
    case _ => println("Invalid day"),
}

// Matching with destructuring
let point = (0, 5);

match point {
    case (0, 0) => println("Origin"),
    case (0, _) => println("On Y-axis"),
    case (_, 0) => println("On X-axis"),
    case (x, y) => println(f"({x}, {y})"),
}
💡 Tip

Match cases use => to separate the pattern from the result. The | operator allows multiple patterns to share the same branch.

Functions

Named Functions

Define functions with the func keyword and a block body:

Zamin
func add(a, b) {
    return a + b;
}

println(add(3, 4)); // 7

Expression Bodies

For short functions, use an expression body instead of a block:

Zamin
func add(a, b) = a + b;
func square(n) = n ** 2;
func greet(name) = f"Hello, {name}!";

println(square(5));  // 25
println(greet("Zamin")); // "Hello, Zamin!"

Lambda / Closure Expressions

Anonymous functions can be assigned to variables:

Zamin
let add = func(a, b) {
    return a + b;
};

let multiply = func(a, b) = a * b;

println(add(2, 3));       // 5
println(multiply(4, 5));  // 20

Default Arguments

Zamin
func greet(name = "World") {
    println(f"Hello, {name}!");
}

greet();        // "Hello, World!"
greet("Alice"); // "Hello, Alice!"

Variadic Arguments

Prefix a parameter with * to accept any number of arguments:

Zamin
func sum(*nums) {
    let total = 0;
    for n in nums {
        total += n;
    }
    return total;
}

println(sum(1, 2, 3, 4)); // 10

Closures

Functions can capture and use variables from their enclosing scope:

Zamin
func make_counter() {
    let count = 0;
    return func() {
        count += 1;
        return count;
    };
}

let counter = make_counter();
println(counter()); // 1
println(counter()); // 2
println(counter()); // 3

Higher-Order Functions

Functions can be passed as arguments to other functions:

Zamin
func apply(f, value) {
    return f(value);
}

let double = func(x) = x * 2;
let triple = func(x) = x * 3;

println(apply(double, 5)); // 10
println(apply(triple, 5)); // 15

let nums = [1, 2, 3, 4, 5];
let doubled = [apply(double, x) for x in nums];
println(doubled); // [2, 4, 6, 8, 10]
💡 Tip

Zamin supports common functional operations like map, filter, and reduce — either as built-in functions or methods on lists. List comprehensions are often preferred for readability.

Structs

Structs are Zamin's primary mechanism for creating custom types with named fields.

Defining and Instantiating

Zamin
struct Point {
    x,
    y,
}

let p = Point(3, 4);
println(p.x);  // 3
println(p.y);  // 4

Init Method

Define an init function on a struct to run custom logic during construction. The init function receives the struct instance and can set default or computed field values.

Zamin
struct Rectangle {
    width,
    height,
}

func Rectangle.init(r) {
    r.area = r.width * r.height;
    r.perimeter = (r.width + r.height) * 2;
}

let rect = Rectangle(10, 5);
println(rect.area);       // 50
println(rect.perimeter);  // 30

Methods

Assign closures to struct fields to create methods:

Zamin
struct Circle {
    radius,
}

func Circle.area(self) = 3.14159 * self.radius ** 2;
func Circle.circumference(self) = 2 * 3.14159 * self.radius;
func Circle.describe(self) = f"Circle with radius {self.radius}";

let c = Circle(5);
println(c.area());           // 78.53975
println(c.circumference());  // 31.4159
println(c.describe());       // "Circle with radius 5"

Struct Comparison

Structs of the same type compare by value when all their fields are comparable:

Zamin
let a = Point(1, 2);
let b = Point(1, 2);
let c = Point(3, 4);

println(a == b); // true
println(a == c); // false
println(a != c); // true
💡 Tip

Structs in Zamin are reference types by default. Two variables assigned from the same struct instance will share the same data. Use explicit copying when independent copies are needed.

Error Handling

Zamin provides structured error handling with try/catch blocks and the throw keyword.

Throwing Errors

Zamin
throw "something went wrong";

// Or use a structured error object
throw Error("invalid input");

Try / Catch

Zamin
try {
    let result = risky_operation();
    println(result);
} catch (e) {
    println(f"Error: {e}");
}

Error Propagation

When a function can fail, errors automatically propagate up the call stack unless caught. This lets you handle errors at the appropriate level.

Zamin
func parse_number(s) {
    let n = int(s);
    if n == nil {
        throw Error(f"'{s}' is not a valid number");
    }
    return n;
}

func process_input(input) {
    let num = parse_number(input);
    return num * 2;
}

try {
    let result = process_input("42");
    println(result); // 84
} catch (e) {
    println(f"Failed: {e}");
}

try {
    let result = process_input("abc");
    println(result); // never reached
} catch (e) {
    println(f"Failed: {e}"); // Failed: 'abc' is not a valid number
}
💡 Tip

Prefer returning error values (like nil) for expected failures and throw for unexpected or programmer errors. This keeps error handling explicit and predictable.

Modules and Imports

Zamin's module system lets you split code across files and reuse shared libraries.

Importing Modules

Zamin
import math
import string
import os

println(math.sqrt(16));   // 4.0
println(math.pi);         // 3.14159...

let upper = string.to_upper("hello");
println(upper); // "HELLO"

Aliased Imports

Zamin
import math as m
import string as str

println(m.sqrt(25)); // 5.0
println(str.to_upper("hi")); // "HI"

Creating Modules with Export

Mark items as export to make them available when the module is imported.

Zamin
// my_module.zamin
export func greet(name) = f"Hello, {name}!";

export let VERSION = "1.0.0";

struct export User {
    name,
    email,
}
Zamin
// main.zamin
import my_module

println(my_module.greet("Alice")); // "Hello, Alice!"
println(my_module.VERSION);        // "1.0.0"

let user = my_module.User("Bob", "bob@example.com");

Module Loading Paths

Zamin searches for modules in these locations, in order:

  1. The current file's directory
  2. The project's lib/ directory
  3. The Zamin standard library path
  4. Paths specified in zamin.json configuration
💡 Tip

Use the zamin project CLI to manage dependencies and module paths. See Project CLI for details.

Comments

Zamin supports single-line comments. Everything after // to the end of the line is ignored by the compiler.

Zamin
// This is a comment
let x = 42; // Inline comment

// Multi-line comments can be done
// by writing multiple single-line comments
// like this.
💡 Tip

Use comments to explain why something is done a certain way, not what the code does. Good code should be self-explanatory; comments are for context and intent.

Operators

Below is a complete reference of all operators in Zamin, listed from highest precedence to lowest.

PrecedenceOperatorDescription
1**Exponentiation
2~, - (unary)Bitwise NOT, unary negation
3*, /, //, %Multiply, divide, integer divide, modulo
4+, -Addition, subtraction
5<<, >>Bitwise shift left, shift right
6&Bitwise AND
7^Bitwise XOR
8|Bitwise OR
9.., ..=Range (exclusive), range (inclusive)
10==, !=, <, >, <=, >=Comparison
11notLogical NOT
12andLogical AND
13orLogical OR
14inMembership test
15=, +=, -=, *=, /=, //=, %=, **=Assignment and compound assignment
Zamin
// Arithmetic
println(2 ** 10);       // 1024
println(7 / 2);         // 3.5
println(7 // 2);        // 3
println(-5.abs());      // 5

// Comparison
println(3 == 3);        // true
println(3 != 4);        // true
println(1 < 2);         // true

// Logical
println(true and false); // false
println(true or false);  // true
println(not false);      // true

// Membership
println(3 in [1, 2, 3]); // true
println("a" in "cat");   // true

// Compound assignment
let x = 10;
x += 5;
x *= 2;
println(x);  // 30
💡 Tip

When in doubt about operator precedence, use parentheses to make your intent explicit. (a + b) * c is always clearer than relying on precedence rules.

Edit this page on GitHub Last updated: July 2026