Examples

Practical Sounio code examples

Examples

Learn Sounio through practical examples covering various domains.

Basic Examples

Hello World

fn main() with IO {
    print("Hello, World!")
}

Variables and Types

fn main() with IO {
    // Immutable
    let name = "Sounio"
    let version = 1.0

    // Mutable
    var count = 0
    count = count + 1

    // With units
    let distance: m = 100.0
    let time: s = 9.58
    let speed = distance / time  // m/s

    print("Speed: " + speed.to_string())
}

Functions with Effects

fn greet(name: String) with IO {
    print("Hello, " + name + "!")
}

fn add(a: i32, b: i32) -> i32 {
    a + b  // No effects needed
}

fn main() with IO {
    greet("World")
    print("Sum: " + add(3, 5).to_string())
}

Epistemic Computing

BMI Calculator with Uncertainty

fn main() with IO {
    let mass: Knowledge<kg> = measure(
        value: 75.3,
        uncertainty: 0.5,
        source: "bathroom_scale"
    )

    let height: Knowledge<m> = measure(
        value: 1.82,
        uncertainty: 0.01,
        source: "tape_measure"
    )

    let bmi = mass / (height * height)

    print("BMI: " + bmi.to_string())
    // Output: BMI: 22.73 ± 0.31 kg/m²

    if bmi.confidence > 0.90 {
        let category = match bmi.value {
            v if v < 18.5 => "Underweight",
            v if v < 25.0 => "Normal",
            v if v < 30.0 => "Overweight",
            _ => "Obese",
        }
        print("Category: " + category)
    }
}

Sensor Fusion

fn fuse_sensors(sensors: Vec<Knowledge<celsius>>) -> Knowledge<celsius> {
    // Weighted average by inverse uncertainty
    let weights = sensors.map(|s| 1.0 / s.uncertainty.pow(2))
    let total_weight = weights.sum()

    let fused_value = sensors
        .zip(weights)
        .map(|(s, w)| s.value * w)
        .sum() / total_weight

    let fused_uncertainty = 1.0 / total_weight.sqrt()

    Knowledge {
        value: fused_value,
        uncertainty: fused_uncertainty,
        provenance: sensors.flat_map(|s| s.provenance).collect(),
        confidence: sensors.map(|s| s.confidence).min(),
    }
}

fn main() with IO {
    let readings = vec![
        measure(value: 23.5, uncertainty: 0.2, source: "sensor_A"),
        measure(value: 23.8, uncertainty: 0.3, source: "sensor_B"),
        measure(value: 23.3, uncertainty: 0.25, source: "sensor_C"),
    ]

    let fused = fuse_sensors(readings)
    print("Fused temperature: " + fused.to_string())
    print("Sources: " + fused.provenance.join(", "))
}

Scientific Computing

Numerical Integration

fn integrate(f: fn(f64) -> f64, a: f64, b: f64, n: i32) -> f64 {
    let h = (b - a) / (n as f64)
    var sum = 0.0

    for i in 0..n {
        let x = a + (i as f64) * h
        sum = sum + f(x) * h
    }

    sum
}

fn main() with IO {
    // Integrate x² from 0 to 1
    let result = integrate(|x| x * x, 0.0, 1.0, 1000)
    print("∫x² dx from 0 to 1 = " + result.to_string())
    // Expected: 0.333...
}

ODE Solver (Runge-Kutta)

fn rk4_step(f: fn(f64, f64) -> f64, t: f64, y: f64, h: f64) -> f64 {
    let k1 = f(t, y)
    let k2 = f(t + h/2.0, y + h*k1/2.0)
    let k3 = f(t + h/2.0, y + h*k2/2.0)
    let k4 = f(t + h, y + h*k3)

    y + h * (k1 + 2.0*k2 + 2.0*k3 + k4) / 6.0
}

fn solve_ode(f: fn(f64, f64) -> f64, y0: f64, t_end: f64, dt: f64) -> Vec<(f64, f64)> {
    var t = 0.0
    var y = y0
    var results = vec![(t, y)]

    while t < t_end {
        y = rk4_step(f, t, y, dt)
        t = t + dt
        results.push((t, y))
    }

    results
}

fn main() with IO {
    // Solve dy/dt = -y, y(0) = 1 (exponential decay)
    let solution = solve_ode(|t, y| -y, 1.0, 5.0, 0.1)

    for (t, y) in solution {
        print("t=" + t.to_string() + ", y=" + y.to_string())
    }
}

Data Processing

CSV Analysis

use std::csv::CsvReader

fn main() with IO {
    let reader = CsvReader::open("measurements.csv")?

    var values: Vec<Knowledge<f64>> = vec![]

    for row in reader {
        let value: f64 = row.parse("value")?
        let uncertainty: f64 = row.parse("uncertainty")?
        let source = row.get("source")?

        values.push(measure(
            value: value,
            uncertainty: uncertainty,
            source: source
        ))
    }

    let mean = values.iter().map(|v| v.value).mean()
    print("Mean value: " + mean.to_string())
}

GPU Computing

Vector Addition

use std::gpu::{kernel, launch, Device}

#[kernel]
fn vector_add(a: &[f32], b: &[f32], c: &![f32]) {
    let i = thread_idx() + block_idx() * block_dim()
    if i < a.len() {
        c[i] = a[i] + b[i]
    }
}

fn main() with IO, GPU {
    let device = Device::default()

    let a = vec![1.0f32; 1000000]
    let b = vec![2.0f32; 1000000]
    var c = vec![0.0f32; 1000000]

    let d_a = device.alloc(&a)
    let d_b = device.alloc(&b)
    let d_c = device.alloc_mut(&mut c)

    launch(vector_add, blocks: 1024, threads: 1024, d_a, d_b, d_c)

    device.sync()
    print("First result: " + c[0].to_string())  // 3.0
}

Effect Handlers

Custom Logging

effect Log {
    fn log(level: Level, msg: String)
}

handler ConsoleLog for Log {
    fn log(level: Level, msg: String) with IO {
        print("[" + level.to_string() + "] " + msg)
    }
}

fn process_data(data: Vec<f64>) with Log {
    perform Log::log(Info, "Processing " + data.len().to_string() + " items")

    for item in data {
        if item < 0.0 {
            perform Log::log(Warn, "Negative value: " + item.to_string())
        }
    }

    perform Log::log(Info, "Processing complete")
}

fn main() with IO {
    handle ConsoleLog {
        process_data(vec![1.0, -2.0, 3.0, -4.0, 5.0])
    }
}

What’s Next?