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
annotations 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 enum 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:

module app
import strings
annotation Returns {
    type
}
let answer = 42
func greet(name) {
    return "Hello, " + name
}
data Person {
    name
    age
}
enum 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 func. Functions can return
any value.

let answer = 42
func greet(name) {
    return "Hello, " + name
}
let message = greet("Zirric")
                        

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

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

Data and enums

Zirric models records with data and tagged unions with enum. Enum cases can
be nested data declarations for structured variants.

data Person {
    name
    age
}
enum 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]
                                

Annotations and capabilities

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

annotation 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 module to declare the namespace
and import to access other modules.

module http
import strings
func 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 annotations instead.

Learn more