dol 0.8.1

DOL (Design Ontology Language) - A declarative specification language for ontology-first development
// ═══════════════════════════════════════════════════════════════
// DOL v0.8.0 Functions Example
// Demonstrates pure functions (fun) and side-effect functions (sex)
// ═══════════════════════════════════════════════════════════════

module functions.example @ 0.8.0

docs {
    Demonstrates the function system in DOL v0.8.0.

    DOL distinguishes between two kinds of functions:

    1. Pure functions (fun):
       - No side effects
       - Given the same inputs, always produce the same output
       - Cannot modify external state, do I/O, or call sex functions
       - Safe to memoize, parallelize, and reason about

    2. Side-effect functions (sex):
       - May have side effects
       - Can do I/O, modify state, access globals
       - Can call both pure and sex functions
       - Must be marked explicitly

    This separation enables the compiler to optimize pure code
    and helps developers reason about their programs.
}

// ═══════════════════════════════════════════════════════════════
// DATA TYPES
// ═══════════════════════════════════════════════════════════════

gen Vector3 {
    has x: f64
    has y: f64
    has z: f64
}

gen Matrix3x3 {
    has m00: f64  has m01: f64  has m02: f64
    has m10: f64  has m11: f64  has m12: f64
    has m20: f64  has m21: f64  has m22: f64
}

gen LogEntry {
    has timestamp: i64
    has level: string
    has message: string
}

// ═══════════════════════════════════════════════════════════════
// PURE FUNCTIONS
// ═══════════════════════════════════════════════════════════════

docs {
    Pure function examples - no side effects, deterministic output.
}

// ─────────────────────────────────────────────────────────────────
// Basic Pure Functions
// ─────────────────────────────────────────────────────────────────

docs {
    Add two vectors component-wise.
}

pub fun vec_add(a: Vector3, b: Vector3) -> Vector3 {
    return Vector3 {
        x: a.x + b.x,
        y: a.y + b.y,
        z: a.z + b.z
    }
}

docs {
    Subtract vector b from vector a.
}

pub fun vec_sub(a: Vector3, b: Vector3) -> Vector3 {
    return Vector3 {
        x: a.x - b.x,
        y: a.y - b.y,
        z: a.z - b.z
    }
}

docs {
    Scale a vector by a scalar value.
}

pub fun vec_scale(v: Vector3, s: f64) -> Vector3 {
    return Vector3 {
        x: v.x * s,
        y: v.y * s,
        z: v.z * s
    }
}

docs {
    Compute the dot product of two vectors.
}

pub fun vec_dot(a: Vector3, b: Vector3) -> f64 {
    return a.x * b.x + a.y * b.y + a.z * b.z
}

docs {
    Compute the cross product of two vectors.
}

pub fun vec_cross(a: Vector3, b: Vector3) -> Vector3 {
    return Vector3 {
        x: a.y * b.z - a.z * b.y,
        y: a.z * b.x - a.x * b.z,
        z: a.x * b.y - a.y * b.x
    }
}

docs {
    Compute the squared length of a vector.
    Useful when you only need to compare magnitudes.
}

pub fun vec_length_squared(v: Vector3) -> f64 {
    return v.x * v.x + v.y * v.y + v.z * v.z
}

// ─────────────────────────────────────────────────────────────────
// Higher-Order Pure Functions
// ─────────────────────────────────────────────────────────────────

docs {
    Apply a transformation function to each component of a vector.
}

pub fun vec_map(v: Vector3, f: fun(f64) -> f64) -> Vector3 {
    return Vector3 {
        x: f(v.x),
        y: f(v.y),
        z: f(v.z)
    }
}

docs {
    Fold/reduce a vector to a single value.
}

pub fun vec_fold(v: Vector3, init: f64, f: fun(f64, f64) -> f64) -> f64 {
    let acc = f(init, v.x)
    let acc = f(acc, v.y)
    let acc = f(acc, v.z)
    return acc
}

// ─────────────────────────────────────────────────────────────────
// Pure String Functions
// ─────────────────────────────────────────────────────────────────

docs {
    Format a vector as a string.
}

pub fun vec_to_string(v: Vector3) -> string {
    return "(" + v.x.to_string() + ", " + v.y.to_string() + ", " + v.z.to_string() + ")"
}

docs {
    Check if a string is empty or whitespace only.
}

pub fun is_blank(s: string) -> bool {
    return s.trim() == ""
}

docs {
    Repeat a string n times.
}

pub fun repeat_string(s: string, n: u32) -> string {
    if n == 0 {
        return ""
    }
    let result = ""
    for i in 0..n {
        result = result + s
    }
    return result
}

// ═══════════════════════════════════════════════════════════════
// SIDE-EFFECT FUNCTIONS (SEX)
// ═══════════════════════════════════════════════════════════════

docs {
    Side-effect function examples - may perform I/O or modify state.
}

// ─────────────────────────────────────────────────────────────────
// I/O Functions
// ─────────────────────────────────────────────────────────────────

docs {
    External print function (platform-specific).
}

sex extern fun print(message: string)

docs {
    External println function.
}

sex extern fun println(message: string)

docs {
    External function to read a line from stdin.
}

sex extern fun read_line() -> string

docs {
    External function to get current timestamp.
}

sex extern fun current_time_millis() -> i64

// ─────────────────────────────────────────────────────────────────
// Logging Functions (SEX)
// ─────────────────────────────────────────────────────────────────

docs {
    Log a message at info level.
}

pub sex fun log_info(message: string) {
    let timestamp = current_time_millis()
    println("[INFO " + timestamp.to_string() + "] " + message)
}

docs {
    Log a message at warning level.
}

pub sex fun log_warn(message: string) {
    let timestamp = current_time_millis()
    println("[WARN " + timestamp.to_string() + "] " + message)
}

docs {
    Log a message at error level.
}

pub sex fun log_error(message: string) {
    let timestamp = current_time_millis()
    println("[ERROR " + timestamp.to_string() + "] " + message)
}

docs {
    Log a vector value for debugging.
}

pub sex fun log_vector(name: string, v: Vector3) {
    // Note: sex function can call pure function vec_to_string
    log_info(name + " = " + vec_to_string(v))
}

// ─────────────────────────────────────────────────────────────────
// Interactive Functions (SEX)
// ─────────────────────────────────────────────────────────────────

docs {
    Prompt user for input and return their response.
}

pub sex fun prompt(question: string) -> string {
    print(question + " ")
    return read_line()
}

docs {
    Ask user a yes/no question.
}

pub sex fun confirm(question: string) -> bool {
    let response = prompt(question + " (y/n)")
    return response == "y" || response == "Y" || response == "yes" || response == "Yes"
}

// ─────────────────────────────────────────────────────────────────
// State-Modifying Functions (SEX)
// ─────────────────────────────────────────────────────────────────

docs {
    Global counter state - requires sex to modify.
}

var global_counter: i64 = 0

docs {
    Increment global counter and return new value.
}

pub sex fun increment_global() -> i64 {
    global_counter = global_counter + 1
    return global_counter
}

docs {
    Reset global counter to zero.
}

pub sex fun reset_global() {
    global_counter = 0
}

docs {
    Get current global counter value.
}

pub sex fun get_global() -> i64 {
    return global_counter
}

// ═══════════════════════════════════════════════════════════════
// MIXING PURE AND SEX FUNCTIONS
// ═══════════════════════════════════════════════════════════════

docs {
    Sex functions can call pure functions freely.
    This allows building complex effectful operations from pure building blocks.
}

pub sex fun demo_vector_operations() {
    let a = Vector3 { x: 1.0, y: 2.0, z: 3.0 }
    let b = Vector3 { x: 4.0, y: 5.0, z: 6.0 }

    // Call pure functions
    let sum = vec_add(a, b)
    let dot = vec_dot(a, b)
    let cross = vec_cross(a, b)

    // Use sex function for output
    log_info("Vector a: " + vec_to_string(a))
    log_info("Vector b: " + vec_to_string(b))
    log_info("a + b: " + vec_to_string(sum))
    log_info("a . b: " + dot.to_string())
    log_info("a x b: " + vec_to_string(cross))
}

// ═══════════════════════════════════════════════════════════════
// FUNCTION TYPES AND CALLBACKS
// ═══════════════════════════════════════════════════════════════

docs {
    Type alias for a pure transformer function.
}

type Transformer = fun(f64) -> f64

docs {
    Type alias for a sex callback with logging.
}

type Callback = sex fun(string) -> ()

docs {
    Apply a series of pure transformations to a value.
}

pub fun apply_transformers(value: f64, transforms: Vec<Transformer>) -> f64 {
    let result = value
    for t in transforms {
        result = t(result)
    }
    return result
}

docs {
    Execute a callback with error handling.
}

pub sex fun with_logging(callback: Callback, message: string) {
    log_info("Starting: " + message)
    callback(message)
    log_info("Completed: " + message)
}