Reference
Examples
Real programs from the repository, every one verified to run under `lira run`. Each snippet below is an actual .li file — the same files the compiler checks when this site is built.
This gallery is a guided tour of the examples/ directory.
The programs are ordered roughly from "first ten minutes with Lira" to
"putting the standard library to work", and each one cross-links to the
deep-dive page where the feature is explained in full. New to the
language? Start with the guide, then come back here
to see the pieces combined.
Basics
The starting point: output, string interpolation, branching, and iteration. These use only built-in syntax — no imports required.
println writes a line to standard output. The smallest
possible Lira program is a script with no main at all.
// Hello World Example
// Demonstrates basic output
println("Hello, World!")
println("Welcome to Lira!")
String interpolation uses ${expr} inside a string
literal, and any expression can go in the braces.
// String interpolation with ${expr}
//
fn main() {
let x = 7
let a = 1
let b = 2
// variable interpolation
println("x=${x}")
// expression interpolation
println("sum=${1 + 2}")
// leading interpolation
println("${x} more")
// escaped \${ stays a literal
println("literal \${x}")
// a lone $ not followed by { stays literal
println("costs $5")
// multiple interpolations
println("a ${a} b ${b} c")
}
if/else and while work the way you
expect, and conditions are plain expressions.
// Control Flow Demo
// Demonstrates if/else, while loops, and nested control flow
// Simple if/else
let x = 42
if x > 0 {
println("x is positive")
} else {
println("x is not positive")
}
// Nested if
let grade = 85
if grade >= 90 {
println("Grade: A")
} else {
if grade >= 80 {
println("Grade: B")
} else {
if grade >= 70 {
println("Grade: C")
} else {
println("Grade: F")
}
}
}
// While loop with counter
println("Counting to 5:")
var count = 1
while count <= 5 {
println(count)
count = count + 1
}
// Nested loops - multiplication table
println("Multiplication table (3x3):")
var row = 1
while row <= 3 {
var col = 1
while col <= 3 {
let product = row * col
println(product)
col = col + 1
}
row = row + 1
}
// FizzBuzz (1-15)
println("FizzBuzz:")
var n = 1
while n <= 15 {
let by3 = n % 3 == 0
let by5 = n % 5 == 0
if by3 && by5 {
println("FizzBuzz")
} else {
if by3 {
println("Fizz")
} else {
if by5 {
println("Buzz")
} else {
println(n)
}
}
}
n = n + 1
}
for … in iterates over arrays. Iterating an empty array
simply runs zero times.
// For-In Loop Tests
// Tests iteration over arrays
println("=== Basic For-In Loop ===")
// Simple array iteration
let numbers = [1, 2, 3, 4, 5]
var sum = 0
for n in numbers {
sum = sum + n
}
println(sum) // 15
// Iterate over empty array (should print nothing)
let empty = []
for x in empty {
println(x)
}
println("after empty") // after empty
println("=== For-In with Different Types ===")
// Array of strings
let fruits = ["apple", "banana", "cherry"]
for fruit in fruits {
println(fruit)
}
// Array of bools
let flags = [true, false, true]
for flag in flags {
println(flag)
}
println("=== Nested For Loops ===")
// Nested iteration
let matrix = [[1, 2], [3, 4], [5, 6]]
var total = 0
for row in matrix {
for val in row {
total = total + val
}
}
println(total) // 21 (1+2+3+4+5+6)
println("=== For-In with Expressions ===")
// Inline array expression
var product = 1
for n in [2, 3, 4] {
product = product * n
}
println(product) // 24
println("=== For-In with Break ===")
// Break from loop
var found = 0
for n in [1, 2, 3, 99, 4, 5] {
if n == 99 {
found = n
break
}
}
println(found) // 99
println("=== For-In with Continue ===")
// Continue in loop - sum only even numbers
var even_sum = 0
for n in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] {
if n % 2 != 0 {
continue
}
even_sum = even_sum + n
}
println(even_sum) // 30 (2+4+6+8+10)
println("=== For-In Counter ===")
// Using loop variable to count
var count = 0
for i in [1, 1, 1, 1, 1] {
count = count + 1
}
println(count) // 5
println("=== All For-In Tests Passed ===")Structs & enums
Lira's data modelling rests on struct (named fields, with
methods) and enum (a closed set of variants, optionally
carrying data). See Types for the full type system.
Structs bundle fields and methods. Methods take self as the
first parameter and are called with dot syntax.
// Struct Example
// Demonstrates struct literals, field access, and methods
// Define structs
struct Point {
x: int
y: int
fn sum(self) -> int {
return self.x + self.y
}
fn add(self, other: Point) -> Point {
return Point {
x: self.x + other.x,
y: self.y + other.y
}
}
}
struct Person {
name: string
age: int
fn greet(self) -> string {
return "Hello, " + self.name
}
}
struct Line {
start: Point
end: Point
}
// Create a struct literal
let point = Point { x: 10, y: 20 }
// Access fields
println("Point x:")
println(point.x)
println("Point y:")
println(point.y)
// Create another struct
let person = Person { name: "Alice", age: 30 }
println("Person name:")
println(person.name)
println("Person age:")
println(person.age)
// Nested struct
let line = Line {
start: Point { x: 0, y: 0 },
end: Point { x: 100, y: 50 }
}
println("Line start x:")
println(line.start.x)
println("Line end y:")
println(line.end.y)
// Test methods
println("Point sum:")
println(point.sum())
let p1 = Point { x: 1, y: 2 }
let p2 = Point { x: 3, y: 4 }
let p3 = p1.add(p2)
println("Added point x:")
println(p3.x)
println("Added point y:")
println(p3.y)
println("Person greeting:")
println(person.greet())
A C-style enum is a set of named variants, constructed with the
:: syntax.
// Enum Tests
// Tests enum definitions and variant access
println("=== Simple Enum Definition ===")
// Simple enum (C-style, no data)
enum Color {
Red,
Green,
Blue
}
// Create enum variants using :: syntax
let red = Color::Red
let green = Color::Green
let blue = Color::Blue
println("Created Color variants")
// Access enum info via __enum and __variant fields
println("red.__enum: " + red.__enum) // Color
println("red.__variant: " + red.__variant) // Red
println("green.__variant: " + green.__variant) // Green
println("blue.__variant: " + blue.__variant) // Blue
println("=== Status Enum ===")
enum Status {
Active,
Inactive,
Pending
}
let s = Status::Active
println("Status: " + s.__variant) // Active
println("=== Comparison ===")
// Compare by variant name
let c1 = Color::Red
let c2 = Color::Red
let c3 = Color::Blue
println("c1.__variant == c2.__variant: " + (c1.__variant == c2.__variant)) // true
println("c1.__variant == c3.__variant: " + (c1.__variant == c3.__variant)) // false
println("=== All Enum Tests Passed ===")
Variants can carry data. Here Option::Some(int) wraps a
value, and a match pulls it back out.
enum Option {
None,
Some(int)
}
fn main() {
// Create enum variants with data
let some_val = Option::Some(42)
let none_val = Option::None
// Pattern match on the enum
match some_val {
Option::Some(x) => println(x)
Option::None => println("none")
}
match none_val {
Option::Some(x) => println(x)
Option::None => println("none")
}
// Test with another value
let another = Option::Some(100)
match another {
Option::Some(n) => println(n)
Option::None => println("none")
}
}
main()Pattern matching
match is an expression: every arm produces a value, and the
checker enforces exhaustiveness. The
Patterns page covers literals, bindings,
wildcards, and guards in depth.
Matching on literal values, with _ as the catch-all.
// Pattern Matching Example
// Demonstrates match expressions with literal and wildcard patterns
fn grade(score: int) -> string {
return match score {
100 => "Perfect!",
90 => "A",
80 => "B",
70 => "C",
60 => "D",
_ => "F"
}
}
// Test literal patterns
println("Testing literal patterns:")
println(grade(100))
println(grade(90))
println(grade(80))
println(grade(75))
println(grade(50))
// Test wildcard
fn always_zero(x: int) -> int {
return match x {
_ => 0
}
}
println("Testing wildcard:")
println(always_zero(42))
println(always_zero(100))
// Test variable binding in pattern
fn double_it(x: int) -> int {
return match x {
n => n * 2
}
}
println("Testing variable binding:")
println(double_it(5))
println(double_it(21))When every enum variant is covered, the match is exhaustive and needs no wildcard — and the checker accepts it.
// Exhaustiveness checking accepts matches that cover all variants, and matches
// that use a wildcard catch-all.
enum Color {
Red,
Green,
Blue
}
// All variants covered explicitly.
fn describe(c: Color) -> string {
return match c {
Color::Red => "red"
Color::Green => "green"
Color::Blue => "blue"
}
}
// Wildcard catch-all makes a partial match exhaustive.
fn covered(c: Color) -> string {
return match c {
Color::Red => "covered"
_ => "other"
}
}
println(describe(Color::Red))
println(covered(Color::Red))
Guards add a boolean condition to an arm with
pattern if cond => …, letting one binding fan out into
several cases.
// Pattern Matching with Guards
// Tests conditional guards on match arms
fn classify_number(n: int) -> string {
return match n {
0 => "zero",
x if x < 0 => "negative",
x if x > 100 => "large",
x if x % 2 == 0 => "even",
_ => "odd"
}
}
println("Testing pattern guards:")
println(classify_number(0)) // zero
println(classify_number(-5)) // negative
println(classify_number(200)) // large
println(classify_number(42)) // even
println(classify_number(7)) // odd
// Test with multiple conditions
fn grade_score(score: int) -> string {
return match score {
s if s >= 90 => "A",
s if s >= 80 => "B",
s if s >= 70 => "C",
s if s >= 60 => "D",
_ => "F"
}
}
println("Testing grade scores:")
println(grade_score(95)) // A
println(grade_score(85)) // B
println(grade_score(75)) // C
println(grade_score(65)) // D
println(grade_score(55)) // F
// Test guard with variable binding and computation
fn fizzbuzz(n: int) -> string {
return match n {
x if x % 15 == 0 => "FizzBuzz",
x if x % 3 == 0 => "Fizz",
x if x % 5 == 0 => "Buzz",
_ => "number"
}
}
println("Testing FizzBuzz:")
println(fizzbuzz(15)) // FizzBuzz
println(fizzbuzz(9)) // Fizz
println(fizzbuzz(10)) // Buzz
println(fizzbuzz(7)) // numberGenerics
Functions, structs, and enums can be parameterized over types with
<T>. See Types for how generic
type parameters are checked.
A generic identity function and a generic Box
struct, reused at different element types.
// Generics Tests
// Tests generic function and struct syntax parsing
println("=== Generic Functions ===")
// Generic identity function
fn identity<T>(x: T) -> T {
return x
}
println("identity int: " + identity(42))
println("identity string: " + identity("hello"))
println("=== Generic Structs ===")
// Generic Box struct
struct Box<T> {
value: T
}
let int_box = Box { value: 100 }
println("box value: " + int_box.value)
println("=== All Generics Tests Passed ===")
Enums are generic too — including multi-parameter variants like
Pair<A, B>.
enum Opt<T> {
Some(T),
None
}
enum Pair<A, B> {
Both(A, B),
Neither
}
fn main() {
// Single-param generic enum
let some_val = Opt::Some(42)
let none_val = Opt::None
match some_val {
Opt::Some(x) => println(x)
Opt::None => println("none")
}
match none_val {
Opt::Some(x) => println(x)
Opt::None => println("none")
}
// Two-param generic enum
let both = Pair::Both(1, "hello")
let neither = Pair::Neither
match both {
Pair::Both(a, b) => println("both " + a + " " + b)
Pair::Neither => println("neither")
}
match neither {
Pair::Both(a, b) => println("both " + a + " " + b)
Pair::Neither => println("neither")
}
// Generic enum with concrete annotation
let o: Opt<int> = Opt::Some(5)
match o {
Opt::Some(n) => println(n)
Opt::None => println("none")
}
}
main()Error handling
Lira models fallible work with Result<T, E> and
missing values with the optional type T?. The ?
operator propagates the error or null case early, so the happy path stays
flat.
divide returns a Result; ? in
calculate unwraps the Ok value or returns the
Err to the caller.
fn divide(a: int, b: int) -> Result<int, string> {
if b == 0 {
return Result::Err("division by zero")
}
return Result::Ok(a / b)
}
fn calculate(x: int, y: int) -> Result<int, string> {
let result = divide(x, y)?
return Result::Ok(result * 10)
}
fn main() {
// Test successful case
let r1 = calculate(100, 10)
match r1 {
Result::Ok(v) => println(v)
Result::Err(e) => println("error: " + e)
}
// Test error propagation
let r2 = calculate(100, 0)
match r2 {
Result::Ok(v) => println(v)
Result::Err(e) => println("error: " + e)
}
}
main()
The same ? operator works on optional types
(int?): it short-circuits to null when the value
is absent.
fn get_some() -> int? {
return 42
}
fn get_none() -> int? {
return null
}
fn try_get_some() -> int? {
let x = get_some()?
return x
}
fn try_get_none() -> int? {
let x = get_none()?
return x
}
fn main() {
let result1 = try_get_some()
if result1 != null {
println(result1)
} else {
println("got null")
}
let result2 = try_get_none()
if result2 != null {
println(result2)
} else {
println("got null")
}
}
main()Concurrency
Fibers, channels, select, and std.sync make up
Lira's Go-shaped concurrency model. The
Concurrency page walks through the whole thing,
including the worker-pool pattern.
Channels are created with chan(n) for a buffered channel.
// Channel Basics Example
// Demonstrates channel creation and basic operations
// Note: Full channel operations require fiber mode
// Create a buffered channel
let ch = chan(5)
println("Channel created with buffer size 5")
// Channel type is available for use
println("Channel operations ready")
select waits on channel operations; a default arm makes it
non-blocking.
// Select Statement Example
// Demonstrates select syntax for channel multiplexing
// Note: Full select execution requires fiber mode
// Create test channels
let ch1 = chan(1)
let ch2 = chan(1)
println("Channels created")
// Select with default case - non-blocking
select {
_ => println("Default case executed (no channels ready)")
}
println("Select test complete")
spawn starts a function call on a fresh fiber. It takes a
call, not a block, and returns nothing.
// Note: Actual fiber execution requires fiber mode in VM
fn worker(id: int) {
println("Worker " + id + " running")
}
fn compute(a: int, b: int) -> int {
return a + b
}
fn main() {
println("Fiber syntax test")
// Spawn a simple function call
spawn worker(1)
println("Worker spawned")
// Spawn a computation
spawn compute(10, 20)
println("Computation spawned")
}
main()
When you genuinely need shared mutable state, import std.sync
gives you a typed IntMutex and a WaitGroup. Two
fibers each increment the counter a thousand times, and the total lands on
exactly 2000 — no lost updates.
// std.sync: Mutex + WaitGroup, no lost updates.
//
// Two fibers each increment a shared IntMutex 1000 times. After joining via
// WaitGroup.wait(2), the total MUST be exactly 2000 -- proving real mutual
// exclusion (a capacity-1 channel admits one holder at a time, so no lost
// updates) and that wait() blocks until both workers signalled done.
//
import std.sync
fn worker(m: IntMutex, wg: WaitGroup, iters: int) {
var i = 0
while i < iters {
let v = m.lock()
m.unlock(v + 1)
i = i + 1
}
wg.done()
}
fn main() {
let m = new_int_mutex(0)
let wg = new_wait_group()
let iters = 1000
spawn worker(m, wg, iters)
spawn worker(m, wg, iters)
wg.wait(2)
let total = m.lock()
m.unlock(total)
println("total: " + total)
println("expected: " + (2 * iters))
}Stdlib in anger
Two programs that lean on real import std.X declarations.
For the full surface area, see the
standard library reference.
The module system exercised end to end: import std.core for
int methods like abs/clamp plus the
free min/max, and more.
// Comprehensive Module System Tests
// Tests all aspects of the import system
println("=== Module System Comprehensive Tests ===")
// ============================================================
// Test 1: Basic stdlib imports
// ============================================================
println("\n--- Test 1: Basic stdlib imports ---")
import std.core
// Test method syntax on int
let neg = -42
println("(-42).abs(): " + neg.abs())
println("min(10, 5): " + min(10, 5))
println("max(10, 5): " + max(10, 5))
let val = 15
println("15.clamp(0, 10): " + val.clamp(0, 10))
// ============================================================
// Test 2: Multiple imports
// ============================================================
println("\n--- Test 2: Multiple imports ---")
import std.fs
let test_path = "/tmp/module_test.txt"
let written = write_file(test_path, "Module test content")
println("write_file: " + written)
let content = read_file(test_path)
println("read_file: " + content)
// ============================================================
// Test 3: Selective imports
// ============================================================
println("\n--- Test 3: Selective imports ---")
import std.io.{debug, now_ms}
debug("time check", 123)
let t1 = now_ms()
println("now_ms returned a value: " + (t1 > 0))
// ============================================================
// Test 4: Using imported functions with local code
// ============================================================
println("\n--- Test 4: Mixed local and imported code ---")
fn calculate_distance(x1: int, y1: int, x2: int, y2: int) -> int {
let dx = x2 - x1
let dy = y2 - y1
return dx.abs() + dy.abs() // Manhattan distance using method syntax
}
println("Manhattan distance (0,0) to (3,4): " + calculate_distance(0, 0, 3, 4)) // 7
fn bounded_value(val: int, low: int, high: int) -> int {
return val.clamp(low, high)
}
println("bounded_value(50, 0, 100): " + bounded_value(50, 0, 100)) // 50
println("bounded_value(150, 0, 100): " + bounded_value(150, 0, 100)) // 100
println("bounded_value(-10, 0, 100): " + bounded_value(-10, 0, 100)) // 0
// ============================================================
// Test 5: File operations integration
// ============================================================
println("\n--- Test 5: File operations integration ---")
fn save_and_load(filename: string, data: string) -> string {
write_file(filename, data)
return read_file(filename)
}
let result = save_and_load("/tmp/module_test2.txt", "Hello from function!")
println("save_and_load result: " + result)
// ============================================================
// Test 6: Core math operations
// ============================================================
println("\n--- Test 6: Core math operations ---")
fn complex_calc(n: int) -> int {
let a = n * n // n^2
let diff = a - 100
let b = diff.abs() // |n^2 - 100|
let c = min(b, 50) // cap at 50
return c
}
println("complex_calc(5): " + complex_calc(5)) // min(|25-100|, 50) = 50
println("complex_calc(10): " + complex_calc(10)) // min(|100-100|, 50) = 0
println("complex_calc(8): " + complex_calc(8)) // min(|64-100|, 50) = 36
// ============================================================
// Test 7: Multiple levels of function calls
// ============================================================
println("\n--- Test 7: Chained function calls ---")
fn outer_fn(x: int) -> int {
return inner_fn(x) + 10
}
fn inner_fn(x: int) -> int {
return x.abs() * 2
}
println("outer_fn(-5): " + outer_fn(-5)) // abs(-5)*2 + 10 = 20
println("\n=== All Module Tests Passed ===")
import std.collections brings array helpers like
sum, product, filter_*,
sort, unique, and range into scope
as methods on arrays.
// Collections Module Tests
import std.collections
println("=== Collections Module Tests ===")
// Test sum
let arr = [1, 2, 3, 4, 5]
let s = arr.sum()
if s == 15 {
println("sum test passed")
}
// Test product
let p = arr.product()
println("product: " + p)
// Test min/max
let mn = arr.min()
let mx = arr.max()
println("min: " + mn + " max: " + mx)
// Test filter_even
let evens = [1, 2, 3, 4, 5, 6].filter_even()
if len(evens) == 3 {
println("filter test passed")
}
// Test sort
let unsorted = [5, 2, 8, 1, 9]
let sorted = unsorted.sort()
if sorted[0] == 1 && sorted[4] == 9 {
println("sort test passed")
}
// Test range
let r = range(0, 5)
if len(r) == 5 && r[0] == 0 && r[4] == 4 {
println("range test passed")
}
// Test unique
let dups = [1, 2, 2, 3, 3, 3]
let uniq = dups.unique()
if len(uniq) == 3 {
println("unique test passed")
}
// Test contains
if arr.contains(3) && !arr.contains(10) {
println("contains test passed")
}
// Test reverse
let rev = [1, 2, 3].reverse()
println("reversed: " + rev[0] + "," + rev[1] + "," + rev[2])
// Test take/skip
let taken = [1, 2, 3, 4, 5].take(3)
let skipped = [1, 2, 3, 4, 5].skip(2)
println("take 3: " + len(taken) + " skip 2: " + len(skipped))
println("=== All Collections Tests Passed ===")
Want more? Every file under examples/ in the repository runs
the same way. Head to the guide for a structured
walkthrough, or the standard library for the full
API.