Getting Started

Zirric is a declaration-driven language with an expression-first feel. It aims
to stay small while keeping enough structure to build real programs. Values are
dynamic, but conversions are explicit, and behavior is described through
attributes rather than interfaces.

Experimental

Zirric is still evolving. Some features are specified but not fully implemented
yet. Use the proposals for authoritative intent.

What Zirric emphasizes

  • Data and union types for structured modeling
  • First-class functions with concise syntax
  • Modules as the unit of organization and import
  • Annotations as the primary capability mechanism
  • Expression-first control flow (if, for)

Declarations at a glance

Zirric code is built from a small set of declarations:

mod app

import strings

attr Returns {
    type
}

let answer = 42

fn greet(name) {
    return "Hello, " + name
}

data Person {
    name
    age
}

union Result {
    data Ok { value }
    data Err { message }
}

Values and literals

Zirric supports basic literals you should be familiar with:

42                 // Int
3.14               // Float
true               // Bool
"Hello"            // String
[1, 2, 3]          // Array
{ "key": "value" } // Dict
{ a, b -> a + b }  // Function literal

Variables and functions

Declare variables with let and functions with fn. Functions can return
any value.

let answer = 42

fn greet(name) {
    return "Hello, " + name
}

let message = greet("Zirric")

Functions are values and can be passed around like any other expression:

fn applyTwice(f, value) {
    return f(f(value))
}

Data and unions

Zirric models records with data and tagged unions with union. Union members can
be nested data declarations for structured variants.

data Person {
    name
    age
}

union Result {
    data Ok { value }
    data Err { message }
}

let person = Person("Avery", 30)
let ok = Ok("Done")

Control flow

if and for come in expression and statement forms. Expression forms return
values; statement forms are for side effects.

let status = if answer == 42 {
    "yes"
} else {
    "no"
}

if answer == 42 {
    print("yes")
} else {
    print("no")
}

for item <- [1, 2, 3] {
    print(item)
}

let oddNumbers = for item <- [1, 2, 3] {
    if item % 2 != 0 {
        item
    } else {
        continue // skip to next iteration
    }
}

// oddNumbers is [1, 3]

Attributes and capabilities

Zirric does not use interfaces. Instead, attributes describe capabilities and
attach metadata to declarations. They are a core part of the language and
tooling story.

attr Countable {
    @Returns(Int)
    length(@Has(Countable) value)
}

@Countable({ v -> v.length })
data Bag {
    items
    length
}

Annotations are central to tooling, defaults, and protocol-like behavior.

Modules and imports

Zirric code is organized into modules. Use mod to declare the namespace
and import to access other modules.

mod http
import strings

fn statusLine(code) {
    return "HTTP " + strings.fromInt(code)
}

What Zirric avoids

  • Interfaces or inheritance as a primary abstraction
  • Implicit conversions between types

Zirric favors explicit declarations and attributes instead.

Learn more