๐Ÿ“– Complete Book ยท 12 Chapters ยท All Examples Runnable

Ovie โ€” The Complete Book

A comprehensive guide to the Ovie programming language. From your first seeAm to production deployment. Free and open source.

Version 2.3.0ยทApril 2026ยทMIT License
Chapter 01

Introduction and Philosophy

The Ovie Vision

Ovie is a low-level programming language with high-level features. It gives you direct control over memory and hardware while keeping the syntax readable and the developer experience friendly.

The name "Ovie" comes from a name common in Southern Nigeria. The language was built to be accessible to developers everywhere โ€” not just those with access to expensive tooling or fast internet.

Why Ovie Exists

  • Offline-first: Build software without an internet connection. All dependencies are vendored locally.
  • Deterministic: The same code always produces the same output.
  • Self-hosted: Ovie compiles itself. Proven language maturity.
  • Accessible syntax: seeAm comes from Nigerian Pidgin English.

Your First Program

hello.ov
seeAm "Hello from Ovie!"

mut name = "Developer"
fn greet(person) {
    seeAm "Hello, " + person + "!"
}
greet(name)
terminal
oviec run hello.ov
# Hello from Ovie!
# Hello, Developer!
Chapter 02

Installation and Setup

Quick Install

bash
curl -sSL https://raw.githubusercontent.com/southwarridev/ovie/main/easy-linux-install.sh | bash
PowerShell
iwr -useb https://raw.githubusercontent.com/southwarridev/ovie/main/easy-windows-install.ps1 | iex
bash
curl -sSL https://raw.githubusercontent.com/southwarridev/ovie/main/easy-macos-install.sh | bash

Verify Installation

terminal
ovie --version       # 2.3.0
oviec --self-check   # Full validation
ovie new my-project
oviec run my-project/src/main.ov

Build from Source

terminal
git clone https://github.com/southwarridev/ovie.git
cd ovie
make build
make install
Chapter 03

Language Fundamentals

Variables

variables.ov
name = "Ovie"        // immutable
mut counter = 0      // mutable
counter = counter + 1
seeAm counter        // 1

Functions

functions.ov
fn add(a: Number, b: Number) -> Number {
    return a + b
}
mut result = add(10, 20)
seeAm result  // 30

Control Flow

control.ov
if x > 5 { seeAm "big" } else { seeAm "small" }

for i in 0..5 { seeAm i }

mut n = 0
while n < 3 { n = n + 1; seeAm n }

Structs and Enums

structs.ov
struct Person { name: String, age: Number }
enum Status { Active, Inactive }

mut p = Person { name: "Amina", age: 28 }
seeAm p.name  // Amina

Error Handling

errors.ov
use std::core::{Result}

fn divide(a: Number, b: Number) -> Result {
    if b == 0 { return Result.Err("Division by zero") }
    return Result.Ok(a / b)
}

mut r = divide(10, 2)
if r.is_ok() { seeAm r.unwrap() }  // 5

The 13 Keywords

fn mut if else for while struct enum unsafe return true false seeAm

Chapter 04

Module System Deep Dive

Exporting

math_utils.ov
/// Add two numbers
/// # Parameters
/// - a: First number
/// - b: Second number
/// # Returns
/// Sum of a and b
/// # Examples
/// ```ovie
/// mut r = add(2, 3)  // 5
/// ```
export fn add(a: Number, b: Number) -> Number {
    return a + b
}
export struct Point { x: Number, y: Number }
// Private โ€” not accessible outside this module
fn helper(n: Number) -> Number { return n * 2 }

Importing

main.ov
use std::math::{sqrt, pow}
use std::io::{println}
use std::core::{Result, Option}
import "./utils.ov"

fn main() {
    mut d = sqrt(16.0)
    seeAm "sqrt(16) = " + d  // 4.0
}

Package Manager

ovie.toml
[package]
name = "my-app"
version = "1.0.0"
[dependencies]
oba = { path = "./oba" }
terminal
ovie init my-package   # Create new package
ovie add some-lib      # Add dependency
ovie install           # Install all dependencies
ovie cache clear       # Force full rebuild

Best Practices

  • One concern per module โ€” keep modules focused
  • Export only what consumers need โ€” private by default
  • Document all exported functions (required by the compiler)
  • Use std:: modules before writing your own utilities
  • Pin dependency versions in ovie.toml for reproducible builds
Chapter 05

Standard Library Reference

Ovie ships with 11 standard library modules. Import them with use std::module_name.

std::core โ€” Core Types

The foundation: Result, Option, Vec, HashMap.

core_example.ov
use std::core::{Result, Option, Vec, HashMap}

// Result โ€” success or failure
fn divide(a: Number, b: Number) -> Result {
    if b == 0 { return Result.Err("Division by zero") }
    return Result.Ok(a / b)
}
mut r = divide(10, 2)
if r.is_ok() { seeAm r.unwrap() }  // 5

// Option โ€” value or nothing
fn find_user(id: Number) -> Option {
    if id == 1 { return Option.Some("Alice") }
    return Option.None()
}
mut u = find_user(1)
if u.is_some() { seeAm u.unwrap() }  // Alice

// Vec โ€” dynamic array
mut v = Vec.new()
v = Vec.push(v, 10)
v = Vec.push(v, 20)
seeAm Vec.length(v)  // 2

// HashMap โ€” key-value store
mut map = HashMap.new()
map = HashMap.insert(map, "name", "Ovie")
seeAm HashMap.get(map, "name")  // Ovie

std::math โ€” Mathematics

Mathematical functions: sqrt, pow, abs, floor, ceil, min, max, round.

math_example.ov
use std::math::{sqrt, pow, abs, floor, ceil, min, max}

seeAm sqrt(16.0)      // 4.0
seeAm pow(2.0, 10.0)  // 1024.0
seeAm abs(-42)        // 42
seeAm floor(3.7)      // 3.0
seeAm ceil(3.2)       // 4.0
seeAm min(5, 3)       // 3
seeAm max(5, 3)       // 5

std::io โ€” Input/Output

Console I/O: println, print, read_line.

io_example.ov
use std::io::{println, print, read_line}

print("Enter your name: ")
mut name = read_line()
println("Hello, " + name + "!")

std::fs โ€” File System

File operations: read_file, write_file, file_exists, make_dir, list_files.

fs_example.ov
use std::fs::{read_file, write_file, file_exists, make_dir}

if file_exists("data.txt") {
    mut content = read_file("data.txt")
    seeAm content
} else {
    write_file("data.txt", "Hello from Ovie!")
}

make_dir("output")
write_file("output/result.txt", "Done!")

std::time โ€” Time Utilities

Time functions: now, duration_ms, sleep_ms.

time_example.ov
use std::time::{now, duration_ms}

mut start = now()
// ... do work ...
mut elapsed = duration_ms(start, now())
seeAm "Elapsed: " + elapsed + "ms"

std::env โ€” Environment

Environment access: get_var, args, hostname.

env_example.ov
use std::env::{get_var, args, hostname}

mut home = get_var("HOME")
mut host = hostname()
mut program_args = args()

seeAm "Running on: " + host
seeAm "HOME: " + home

std::cli โ€” Command Line

CLI utilities: parse_args, flag_value, print_usage.

cli_example.ov
use std::cli::{parse_args, flag_value}
use std::env::{args}

mut cli = parse_args(args())
mut output = flag_value(cli, "--output", "result.txt")
seeAm "Output file: " + output

std::log โ€” Structured Logging

Log levels: debug, info, warn, error. Configure minimum level in .ovie/aproko.toml.

log_example.ov
use std::log::{info, warn, error, debug}

info("Server started on port 8080")
warn("Config file not found, using defaults")
error("Failed to connect to database")
debug("Processing item: " + item_id)

std::testing โ€” Test Framework

Assertions: assert_eq, assert_true, assert_false, assert_not_null.

test_example.ov
use std::testing::{assert_eq, assert_true, assert_false}

fn test_add() {
    assert_eq(add(2, 3), 5, "2 + 3 should equal 5")
    assert_true(add(0, 0) == 0, "0 + 0 should be 0")
    assert_false(add(1, 1) == 3, "1 + 1 should not be 3")
}

test_add()
seeAm "All tests passed"

std::module โ€” Module System (New v2.3)

Module loading, resolution, dependency graph, cache, and package manager.

module_example.ov
use std::module::{load_module, resolve, kb_query}

// Load a module dynamically
mut mod = load_module("./plugins/my_plugin.ov")

// Query the knowledge base
mut types = kb_query({
    category: "TypeInformation",
    symbol_name: "add"
})

std::aproko โ€” Knowledge Base (New v2.3)

Persistent AI-accessible storage for type info, patterns, and reasoning rules.

aproko_example.ov
use std::aproko::{store_entry, get_type_info, kb_entry_new, export_json}

// Store analysis result
mut entry = kb_entry_new(
    "my_fn",
    "TypeInformation",
    "{\"returns\":\"Number\"}"
)
entry.symbol_name = "my_fn"
store_entry(entry)

// Query type info
mut info = get_type_info("my_fn")

// Export for AI/LLM consumption
export_json(".ovie/aproko/export.json")
Chapter 06

Aproko Integration

What is Aproko?

Aproko is Ovie's built-in static analysis and reasoning engine. It runs during compilation and provides feedback across six categories: Syntax, Logic, Performance, Security, Correctness, and Style.

The name "Aproko" comes from Nigerian slang for someone who notices everything โ€” fitting for a tool that watches your code closely.

Running Analysis

terminal
oviec analyze my_program.ov
# [WARNING] Style: Variable 'x' is single-character (line 3)
# [ERROR] Security: Unsafe block without justification (line 12)
# [INFO] Performance: Loop could use early exit (line 20)

Configuration

.ovie/aproko.toml
[analysis]
min_severity = "Warning"   # Info | Warning | Error | Critical

[categories]
syntax = true
logic = true
performance = true
security = true
correctness = true
style = false              # Disable style checks

Knowledge Base

Aproko stores analysis results in .ovie/aproko/knowledge/ โ€” a persistent knowledge base that AI tools and LLMs can query.

terminal
# Query knowledge base
ovie aproko query --category TypeInformation --symbol add

# Export for AI/LLM consumption
ovie aproko export --output knowledge.json

# Check documentation completeness
ovie doc check

# Generate API docs
ovie doc --format html

Aproko and the Module System

When analyzing modular programs, Aproko loads all imported modules, tracks data flow across boundaries, detects unused imports, verifies exported symbols are documented, and checks for breaking API changes.

unused_import.ov
use std::math::{sqrt}
use std::io::{println}

fn main() {
    println("Hello")
    // sqrt imported but unused โ€” Aproko will flag this
}
Chapter 07

Building Production Applications

Project Structure

project layout
my-app/
โ”œโ”€โ”€ ovie.toml          # Project manifest
โ”œโ”€โ”€ ovie.lock          # Dependency lock file
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ main.ov        # Entry point
โ”‚   โ”œโ”€โ”€ config.ov      # Configuration
โ”‚   โ””โ”€โ”€ handlers/
โ”‚       โ””โ”€โ”€ mod.ov
โ”œโ”€โ”€ tests/
โ”‚   โ””โ”€โ”€ integration.ov
โ””โ”€โ”€ docs/api/          # Generated by ovie doc

Configuration Management

config.ov
use std::env::{get_var}

struct AppConfig {
    port: Number,
    db_path: String,
    log_level: String,
}

fn load_config() -> AppConfig {
    return AppConfig {
        port: string_to_number(get_var("PORT")),
        db_path: get_var("DB_PATH"),
        log_level: get_var("LOG_LEVEL"),
    }
}

Error Handling Strategy

handlers.ov
use std::core::{Result}
use std::log::{info, error}

fn process_request(id: String, data: String) -> Result {
    info("Processing: " + id)
    mut result = handle(data)
    if result.is_err() {
        error("Failed: " + id + " โ€” " + result.unwrap_err())
        return result
    }
    info("Complete: " + id)
    return result
}

Security Best Practices

  • Never hardcode secrets โ€” use environment variables
  • Validate all external input before processing
  • Use unsafe blocks only when necessary, with a comment explaining why
  • Keep dependencies minimal and pinned in ovie.lock
  • Run oviec analyze before every release

Pre-Release Checklist

terminal
ovie test
oviec analyze src/main.ov
ovie doc check
oviec --self-check
oviec build --backend wasm src/main.ov
Chapter 08

Testing and Quality Assurance

Unit Testing

test_math.ov
use std::testing::{assert_eq, assert_true}

fn add(a: Number, b: Number) -> Number { return a + b }

fn test_add_positive() {
    assert_eq(add(2, 3), 5, "2 + 3 = 5")
    assert_eq(add(0, 0), 0, "0 + 0 = 0")
}

fn test_add_negative() {
    assert_eq(add(-1, 1), 0, "-1 + 1 = 0")
}

test_add_positive()
test_add_negative()
seeAm "All tests passed"
terminal
ovie test

Property-Based Testing

Property tests verify that a function satisfies a universal property across many inputs โ€” not just specific examples.

property_test.ov
use std::testing::{assert_true}

// Property: add is commutative
fn prop_add_commutative(a: Number, b: Number) -> Boolean {
    return add(a, b) == add(b, a)
}

// Test with many values
mut i = -50
while i <= 50 {
    mut j = -50
    while j <= 50 {
        assert_true(prop_add_commutative(i, j), "add must be commutative")
        j = j + 1
    }
    i = i + 1
}
seeAm "Commutativity property holds"

Integration Testing

integration_test.ov
use std::fs::{write_file, read_file, file_exists}
use std::testing::{assert_eq, assert_true}

fn test_file_roundtrip() {
    mut path = "/tmp/test_ovie.txt"
    mut content = "Hello from Ovie!"
    write_file(path, content)
    assert_true(file_exists(path), "File should exist after write")
    mut read_back = read_file(path)
    assert_eq(read_back, content, "Read content should match written content")
}

test_file_roundtrip()

Quality Checklist

  • All tests pass: ovie test
  • No Error/Critical Aproko findings: oviec analyze
  • All exported functions documented: ovie doc check
  • Build is reproducible: oviec --self-check
Chapter 09

Performance Optimization

Measure First

Never optimize without measuring. Guessing where the bottleneck is wastes time.

benchmark.ov
use std::time::{now, duration_ms}

fn benchmark(label: String, iterations: Number, work: Function) {
    mut start = now()
    mut i = 0
    while i < iterations {
        work()
        i = i + 1
    }
    mut elapsed = duration_ms(start, now())
    seeAm label + ": " + elapsed + "ms for " + iterations + " iterations"
}

Memory Optimization

memory.ov
// Slow: creates new string on every iteration
fn build_slow(n: Number) -> String {
    mut result = ""
    mut i = 0
    while i < n {
        result = result + number_to_string(i) + ","
        i = i + 1
    }
    return result
}

// Better: collect into array, join once
fn build_fast(n: Number) -> String {
    mut parts = []
    mut i = 0
    while i < n {
        parts = array_push(parts, number_to_string(i))
        i = i + 1
    }
    return array_join(parts, ",")
}

Algorithmic Optimization

Choose the right algorithm. An O(nยฒ) algorithm on 10,000 items is 100x slower than O(n log n).

lookup.ov
use std::core::{HashMap}

// O(1) average โ€” use HashMap for repeated lookups
fn build_lookup(items: Array) -> HashMap {
    mut map = HashMap.new()
    mut i = 0
    while i < array_length(items) {
        map = HashMap.insert(map, array_get(items, i), true)
        i = i + 1
    }
    return map
}

Compiler Optimizations

Use the WASM backend for production โ€” it applies dead code elimination, constant folding, and function inlining:

terminal
oviec build --backend wasm src/main.ov

Module Cache Performance

The module cache makes incremental builds 10x faster. The standard library loads in under 100ms. Don't clear the cache unnecessarily.

Common Pitfalls

  • String concatenation in loops โ€” use array + join instead
  • Repeated file reads โ€” cache the result in a variable
  • Unnecessary module reloads โ€” trust the cache
  • Unneeded unsafe blocks โ€” they bypass optimizations
  • Large dependency trees โ€” keep ovie.toml lean
Chapter 10

Deployment Strategies

Building for Production

terminal
ovie test
oviec analyze src/main.ov
ovie doc check
oviec --self-check
oviec build --backend wasm src/main.ov -o dist/app.wasm
oviec build-package  # Creates platform packages in target/dist/

Cross-Platform Targets

  • wasm โ€” WebAssembly (runs anywhere with a WASM runtime)
  • llvm โ€” Native binary via LLVM
  • interpreter โ€” Direct execution (development only)

Offline-First Deployment

Ovie is designed for offline-first deployment. All dependencies are vendored โ€” no network calls at runtime. Verify offline capability:

terminal
# Disconnect from network, then:
ovie build
ovie test
# Both should succeed without network

Containerization

Dockerfile
FROM ubuntu:22.04
COPY linux-x64/ovie /usr/local/bin/ovie
COPY linux-x64/oviec /usr/local/bin/oviec
COPY linux-x64/std /usr/local/lib/ovie/std
COPY src/ /app/src/
COPY ovie.toml /app/
WORKDIR /app
CMD ["oviec", "run", "src/main.ov"]

Rollback Procedures

The ovie.lock file pins exact versions. To rollback: restore the previous ovie.lock from version control, run ovie install, rebuild and redeploy. Always commit ovie.lock to version control.

Chapter 11

Community and Contribution

The Ovie Community

Development Setup

terminal
git clone https://github.com/southwarridev/ovie.git
cd ovie
make dev-setup
make dev  # clean + build + test

Documentation Standards

All exported functions must have doc comments. The compiler enforces this:

doc_format.ov
/// Brief one-line summary
///
/// Longer description if needed.
///
/// # Parameters
/// - param_name: Description of the parameter
///
/// # Returns
/// Description of the return value
///
/// # Examples
/// ```ovie
/// mut result = my_function(42)
/// seeAm result
/// ```
export fn my_function(x: Number) -> Number {
    return x * 2
}

Areas for Contribution

  • Core language features and bug fixes
  • Standard library expansion
  • Aproko reasoning rules
  • Documentation and examples
  • Testing and benchmarks
  • IDE integration

Community Guidelines

  • Be respectful and constructive
  • Help newcomers โ€” everyone was a beginner once
  • Report bugs with minimal reproducible examples
  • Discuss breaking changes before implementing them
  • Credit contributors in commit messages
Chapter 12

Developer Stories

Building for Real Constraints

Ovie was built with real-world constraints in mind: intermittent power, limited bandwidth, shared devices. The offline-first design isn't an academic exercise โ€” it's a practical response to how software gets built in many parts of the world.

These constraints shaped Ovie's core principles. What started as practical necessity became a design philosophy: software should work everywhere, not just in well-connected offices.

What Developers Are Building

Ovie is being used to build:

  • Agricultural data tools that work offline in the field
  • School management systems for institutions with unreliable connectivity
  • Local language processing tools
  • Community resource tracking applications
  • Educational programming environments for students

The Self-Hosting Achievement

When Ovie compiled itself for the first time, it was more than a technical milestone. It was proof that a language built by and for a specific community could reach the level of maturity that systems programming languages demand.

Self-hosting means the language is real. It means the compiler is tested against itself. It means the community that built it trusts it enough to use it for the most critical task: building the next version of itself.

The seeAm Keyword

The seeAm keyword โ€” from Nigerian Pidgin English โ€” is a small signal that this language was built with a specific community in mind. Programming languages don't have to feel foreign. They can carry the identity of the people who built them.

A Vision for the Future

The goal isn't to replace other languages. It's to give developers everywhere โ€” especially those working with real constraints โ€” a tool that respects their context. A language that works offline. That explains itself. That doesn't require expensive infrastructure to use.

Call to Action

  • Build something. Anything. A tool that solves a problem you have.
  • Document it. Write the doc comments. Run ovie doc.
  • Share it. Put it on GitHub. Tell someone about it.
  • Contribute back. Fix a bug. Improve a doc. Add an example.

The language gets better when more people use it and contribute to it. That's how open source works. That's how communities grow.

"Low-level control meets high-level productivity โ€” systems programming made accessible."
๐Ÿš€

Ready to Start Building?

Download Ovie v2.3 and start writing systems software with high-level productivity.