Skip to content

Chapter 15: Real-World Applications

From Exercises to Production

You've learned the language. You've seen the features. Now let's build something real.

This chapter walks through five complete applications. Each demonstrates different aspects of Wyn and solves actual problems. The code is production-quality-error handling, edge cases, documentation.

Study these. Modify them. Build your own versions. This is where theory becomes practice.

Project 1: Command-Line Todo App

A todo list manager with persistence.

Features

  • Add, list, and complete todos
  • Save to a text file
  • Command-line interface

Implementation

wyn
struct Todo {
    id: int,
    text: string,
    done: bool
}

struct TodoList {
    todos: [Todo],
    next_id: int
}

fn TodoList.add(self, text: string) -> TodoList {
    var todos = self.todos
    todos.push(Todo { id: self.next_id, text: text, done: false })
    return TodoList { todos: todos, next_id: self.next_id + 1 }
}

fn TodoList.has(self, id: int) -> bool {
    for (var i = 0; i < self.todos.len(); i = i + 1) {
        if (self.todos[i].id == id) {
            return true
        }
    }
    return false
}

fn TodoList.complete(self, id: int) -> TodoList {
    var todos = self.todos
    for (var i = 0; i < todos.len(); i = i + 1) {
        if (todos[i].id == id) {
            todos[i].done = true
        }
    }
    return TodoList { todos: todos, next_id: self.next_id }
}

fn TodoList.list(self) {
    if (self.todos.len() == 0) {
        print("No todos")
        return
    }
    
    for (var i = 0; i < self.todos.len(); i = i + 1) {
        var todo = self.todos[i]
        var status = if (todo.done) { "[x]" } else { "[ ]" }
        print("${todo.id}. ${status} ${todo.text}")
    }
}

fn TodoList.save(self, path: string) {
    var lines = ""
    for (var i = 0; i < self.todos.len(); i = i + 1) {
        var t = self.todos[i]
        var d = if (t.done) { "1" } else { "0" }
        lines = lines + "${t.id}|${d}|${t.text}\n"
    }
    File::write(path, lines)
}

fn load_todos(path: string) -> TodoList {
    var list = TodoList { todos: [], next_id: 1 }
    if (not File::exists(path)) {
        return list
    }
    
    var contents = File::read(path)
    var lines = contents.split("\n")
    for (var i = 0; i < lines.len(); i = i + 1) {
        var line = lines[i]
        if (line.len() == 0) { continue }
        
        var id = line.split_at("|", 0).to_int()
        var done = line.split_at("|", 1) == "1"
        var text = line.split_at("|", 2)
        
        list.todos.push(Todo { id: id, text: text, done: done })
        if (id >= list.next_id) {
            list.next_id = id + 1
        }
    }
    return list
}

fn main() -> int {
    var args = System::args()
    
    if (args.len() < 2) {
        print("Usage: todo <command> [args]")
        print("Commands: add, list, done")
        return 1
    }
    
    var list = load_todos("todos.txt")
    var command = args[1]
    
    if (command == "add") {
        if (args.len() < 3) {
            print("Usage: todo add <text>")
            return 1
        }
        list = list.add(args[2])
        print("Added todo")
    } else if (command == "list") {
        list.list()
    } else if (command == "done") {
        if (args.len() < 3) {
            print("Usage: todo done <id>")
            return 1
        }
        if (not args[2].is_numeric()) {
            print("Invalid ID")
            return 1
        }
        var id = args[2].to_int()
        if (not list.has(id)) {
            print("Error: Todo not found")
            return 1
        }
        list = list.complete(id)
        print("Marked as done")
    } else {
        print("Unknown command: " + command)
        return 1
    }
    
    list.save("todos.txt")
    return 0
}

Usage

bash
wyn build todo.wyn
./todo add "Buy groceries"
./todo add "Write code"
./todo list
./todo done 1
./todo list

Key Lessons

  • Struct methods organize related functionality; methods return an updated struct (value semantics)
  • Input validation (is_numeric, has) turns bad input into clear messages
  • File persistence saves state between runs with a simple line format
  • Command parsing creates a CLI interface

Project 2: HTTP Web Server

A simple web server with routing and JSON API.

Features

  • Route handling
  • JSON request/response
  • Static file serving
  • Error handling

Implementation

wyn
struct Request {
    method: string,
    path: string
}

struct Response {
    status: int,
    content_type: string,
    body: string
}

// Route handlers

fn handle_home(req: Request) -> Response {
    return Response {
        status: 200,
        content_type: "text/html",
        body: "<h1>Welcome to Wyn Server</h1>"
    }
}

fn handle_api_users(req: Request) -> Response {
    var users = "[{\"id\": 1, \"name\": \"Alice\"}, {\"id\": 2, \"name\": \"Bob\"}]"
    return Response {
        status: 200,
        content_type: "application/json",
        body: users
    }
}

fn serve_static(req: Request) -> Response {
    var file_path = "public" + req.path
    if (File::exists(file_path)) {
        return Response {
            status: 200,
            content_type: "text/html",
            body: File::read(file_path)
        }
    }
    return Response {
        status: 404,
        content_type: "text/plain",
        body: "Not Found"
    }
}

fn dispatch(req: Request) -> Response {
    if (req.path == "/") {
        return handle_home(req)
    }
    if (req.path == "/api/users") {
        return handle_api_users(req)
    }
    return serve_static(req)
}

fn main() -> int {
    var port = 8080
    var server = Http.serve(port)
    print("Server running on http://localhost:${port}")
    
    while (true) {
        // Http.accept blocks until a request arrives:
        // returns "METHOD|path|headers|fd"
        var raw = Http.accept(server)
        var req = Request {
            method: raw.split_at("|", 0),
            path: raw.split_at("|", 1)
        }
        var fd = raw.split_at("|", 3).to_int()
        
        var resp = dispatch(req)
        Http.respond(fd, resp.status, resp.content_type, resp.body)
    }
    
    return 0
}

Key Lessons

  • Routing maps paths to handler functions
  • JSON APIs return structured data
  • Static files serve HTML/CSS/JS with an existence check
  • Http.serve / Http.accept / Http.respond form the accept loop

Project 3: File Processing Pipeline

Read a file, transform it line by line, write the result.

Features

  • Process files line by line
  • Transform data
  • Write results
  • Progress reporting

Implementation

wyn
struct Pipeline {
    input_path: string,
    output_path: string,
    transform: string
}

// Transformations

fn uppercase_transform(line: string) -> string {
    return line.upper()
}

fn csv_to_json_transform(line: string) -> string {
    var parts = line.split(",")
    if (parts.len() < 2) {
        return "{}"
    }
    return "{\"name\": \"${parts[0]}\", \"value\": \"${parts[1]}\"}"
}

fn filter_transform(line: string) -> string {
    if (line.contains("ERROR")) {
        return line
    }
    return ""
}

fn apply_transform(name: string, line: string) -> string {
    if (name == "uppercase") { return uppercase_transform(line) }
    if (name == "csv2json") { return csv_to_json_transform(line) }
    return filter_transform(line)
}

fn Pipeline.process(self) -> Result<int, string> {
    if (not File::exists(self.input_path)) {
        return Err("input file not found: " + self.input_path)
    }
    
    var input = File::read(self.input_path)
    var lines = input.split("\n")
    
    var output = ""
    var processed = 0
    
    for (var i = 0; i < lines.len(); i = i + 1) {
        var transformed = apply_transform(self.transform, lines[i])
        output = output + transformed + "\n"
        
        processed = processed + 1
        if (processed % 1000 == 0) {
            print("Processed ${processed} lines")
        }
    }
    
    File::write(self.output_path, output)
    print("Done! Processed ${processed} lines")
    
    return Ok(processed)
}

fn main() -> int {
    var args = System::args()
    
    if (args.len() < 4) {
        print("Usage: pipeline <input> <output> <transform>")
        print("Transforms: uppercase, csv2json, filter")
        return 1
    }
    
    var transform = args[3]
    if (transform != "uppercase" and transform != "csv2json" and transform != "filter") {
        print("Unknown transform: " + transform)
        return 1
    }
    
    var pipeline = Pipeline {
        input_path: args[1],
        output_path: args[2],
        transform: transform
    }
    
    match pipeline.process() {
        Ok(_) => return 0,
        Err(msg) => {
            print("Error: " + msg)
            return 1
        }
    }
    
    return 0
}

Key Lessons

  • Pipeline pattern chains transformations
  • Progress reporting shows status
  • Dispatch by name selects a transformation at runtime
  • Result returns make failures explicit at the call site

Project 4: Concurrent Web Scraper

Fetch multiple URLs in parallel.

Features

  • Concurrent HTTP requests
  • Rate limiting
  • Result aggregation
  • Error handling

Implementation

wyn
fn scrape_url(url: string) -> string {
    var body = Http.get(url)
    
    // Encode result as "url|bytes" ("url|-1" on failure)
    if (body.len() > 0) {
        return "${url}|${body.len()}"
    }
    return "${url}|-1"
}

fn main() -> int {
    var urls = [
        "https://example.com/page1",
        "https://example.com/page2",
        "https://example.com/page3",
        "https://example.com/page4",
        "https://example.com/page5"
    ]
    var max_concurrent = 2
    
    print("Scraping ${urls.len()} URLs...")
    
    var success = 0
    var failed = 0
    
    // Process in batches of max_concurrent to limit load
    var start = 0
    while (start < urls.len()) {
        var futures = []
        var end = min(start + max_concurrent, urls.len())
        
        for (var i = start; i < end; i = i + 1) {
            futures.push(spawn scrape_url(urls[i]))
        }
        
        var results = await_all(futures)
        
        for (var i = 0; i < results.len(); i = i + 1) {
            var url = urls[start + i]
            var r = results[i]
            var size = r.split_at("|", 1).to_int()
            
            if (size >= 0) {
                success = success + 1
                print("ok   ${url} (${size} bytes)")
            } else {
                failed = failed + 1
                print("FAIL ${url}")
            }
        }
        
        start = end
    }
    
    print("Success: ${success}")
    print("Failed: ${failed}")
    
    return 0
}

Key Lessons

  • Concurrency speeds up I/O-bound tasks
  • Batching (max_concurrent futures at a time) limits load on the remote server
  • await_all collects every batch's results in order
  • Error handling per-request via an encoded status

Project 5: System Monitor

Monitor CPU, memory, and disk usage.

Implementation

wyn
struct SystemStats {
    cpu_percent: float,
    memory_used: int,
    memory_total: int,
    disk_used: int,
    disk_total: int
}

fn get_system_stats() -> SystemStats {
    // Simplified - a real version would parse /proc or call system tools
    return SystemStats {
        cpu_percent: 45.2,
        memory_used: 8192,
        memory_total: 16384,
        disk_used: 250000,
        disk_total: 500000
    }
}

fn format_bytes(bytes: int) -> string {
    if (bytes < 1024) {
        return "${bytes} B"
    }
    if (bytes < 1024 * 1024) {
        return "${bytes / 1024} KB"
    }
    if (bytes < 1024 * 1024 * 1024) {
        return "${bytes / (1024 * 1024)} MB"
    }
    return "${bytes / (1024 * 1024 * 1024)} GB"
}

fn display_stats(stats: SystemStats) {
    print("=== System Monitor ===")
    print("CPU: ${stats.cpu_percent}%")
    
    var mem_percent = (stats.memory_used * 100) / stats.memory_total
    print("Memory: ${format_bytes(stats.memory_used)} / ${format_bytes(stats.memory_total)} (${mem_percent}%)")
    
    var disk_percent = (stats.disk_used * 100) / stats.disk_total
    print("Disk: ${format_bytes(stats.disk_used)} / ${format_bytes(stats.disk_total)} (${disk_percent}%)")
}

fn main() -> int {
    print("System Monitor - Press Ctrl+C to exit")
    
    while (true) {
        var stats = get_system_stats()
        display_stats(stats)
        
        Time::sleep(1000)  // Update every second
        print("")
    }
    
    return 0
}

Key Lessons

  • Struct modeling groups related measurements
  • Formatting makes data readable
  • Loops + Time::sleep create real-time updates
  • String interpolation builds clean output

What You've Learned

Real applications combine everything: structs, error handling, I/O, concurrency, modules. They handle edge cases, validate input, and provide useful error messages.

Study these examples. They're not toy programs-they're patterns you'll use in production. Modify them. Extend them. Build your own.

The best way to learn is to build. Start with something small. Make it work. Make it better. Repeat.

Where to Go From Here

You've learned Wyn from basics to building production applications. You understand the language, the patterns, and the philosophy behind it.

One language for everything. That was the goal. No more switching between Python for scripts, C++ for performance, Go for services, JavaScript for web. Just Wyn-simple enough for quick tasks, powerful enough for complex systems.

After 20 years of context-switching between languages, I created Wyn to prove that simplicity and power aren't opposites. You don't need complexity to build reliable, fast systems. You just need the right tool.

Now you have it. Go build something amazing.


Next: Chapter 16: Python Libraries

MIT License - v1.19.1