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
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
wyn build todo.wyn
./todo add "Buy groceries"
./todo add "Write code"
./todo list
./todo done 1
./todo listKey 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
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.respondform 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
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
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_concurrentfutures at a time) limits load on the remote server await_allcollects 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
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::sleepcreate 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.