Skip to content

Chapter 14: Standard Library

What's Included

Wyn's standard library gives you the tools to build real applications without external dependencies. It's not as large as Python's or as comprehensive as Rust's, but it covers the essentials.

This chapter is a tour of what's available. For complete API documentation, see Appendix B.

String Operations

You've seen basic string methods. Here's the full set:

Inspection

wyn
text = "Hello, World!"

text.len()                    // 13
text.is_empty()               // false
text.contains("World")        // true
text.starts_with("Hello")     // true
text.ends_with("!")           // true

Transformation

wyn
text = "  Hello  "

text.trim()          // "Hello"
text.trim_left()     // "Hello  "
text.trim_right()    // "  Hello"
text.upper()         // "  HELLO  "
text.lower()         // "  hello  "
text.reverse()       // "  olleH  "

Searching

Index methods return the position, or -1 if not found:

wyn
text = "Hello, World!"

text.index_of("o")           // 4
text.last_index_of("o")      // 8
text.index_of("xyz")         // -1

Splitting and Joining

wyn
text = "apple,banana,cherry"

parts = text.split(",")  // ["apple", "banana", "cherry"]

joined = parts.join(" - ")  // "apple - banana - cherry"

Substring

wyn
text = "Hello, World!"

text.substring(0, 5)    // "Hello"
text.substring(7)       // "World!"

Replacement

wyn
text = "Hello, World!"

text.replace("World", "Wyn")     // "Hello, Wyn!"
text.replace_all("l", "L")       // "HeLLo, WorLd!"

Parsing

wyn
num_str = "42"
float_str = "3.14"

num_str.to_int()        // 42
float_str.to_float()    // 3.14
"abc".to_int()          // 0 (unparseable -> 0)

Unparseable strings convert to 0-validate with is_numeric() first when you need to tell "0" apart from garbage.

File I/O

File operations live in the File namespace. File::read returns the contents as a string (empty if the file is missing), so guard with File::exists:

Reading Files

wyn
fn main() -> int {
    if (not File::exists("data.txt")) {
        print("data.txt not found")
        return 1
    }
    
    contents = File::read("data.txt")
    print("File contents:")
    print(contents)
    
    return 0
}

Writing Files

wyn
fn main() -> int {
    data = "Hello, file!"
    ok = File::write("output.txt", data)
    
    if (ok == 1) {
        print("File written")
    } else {
        print("Error writing file")
    }
    
    return 0
}

File::write returns 1 on success, 0 on failure.

Appending to Files

wyn
fn main() -> int {
    ok = File::append("log.txt", "New log entry\n")
    
    if (ok == 1) {
        print("Appended")
    } else {
        print("Error appending")
    }
    
    return 0
}

File Metadata

wyn
fn main() -> int {
    if (File::exists("data.txt")) {
        print("File exists")
        
        size = File::size("data.txt")
        print("Size: ${size}")
    }
    
    return 0
}

Directory Operations

wyn
fn main() -> int {
    // List files in directory
    files = Dir::list(".")
    for (var i = 0; i < files.len(); i = i + 1) {
        print(files[i])
    }
    
    // Create directory
    Dir::create("new_folder")
    
    // Remove directory
    Dir::remove("old_folder")
    
    return 0
}

Networking

HTTP Client

Http.get returns the response body as a string (empty on failure):

wyn
fn main() -> int {
    body = Http.get("https://api.example.com/data")
    
    if (body.len() > 0) {
        print("Body: " + body)
    } else {
        print("Request failed")
    }
    
    return 0
}

HTTP POST

wyn
fn main() -> int {
    data = {"name": "Alice", "age": "30"}
    body = Http.post("https://api.example.com/users", JSON::stringify(data))
    
    print("Created: " + body)
    
    return 0
}

HTTP Server

Http.serve(port) starts a server; Http.accept blocks until a request arrives and returns it as a method|path|headers|fd string:

wyn
fn main() -> int {
    server = Http.serve(8080)
    print("Server running on http://localhost:8080")
    
    while (true) {
        req = Http.accept(server)
        path = req.split_at("|", 1)
        fd = req.split_at("|", 3).to_int()
        
        if (path == "/") {
            Http.respond(fd, 200, "text/plain", "Hello, World!")
        } else {
            Http.respond(fd, 404, "text/plain", "Not Found")
        }
    }
    
    return 0
}

JSON

Parsing JSON

wyn
fn main() -> int {
    json_str = "{\"name\": \"Alice\", \"age\": 30}"
    json = json_parse(json_str)
    
    print(json["name"])  // "Alice"
    print(json["age"])   // 30
    
    return 0
}

Generating JSON

wyn
fn main() -> int {
    data = {
        "name": "Alice",
        "role": "admin"
    }
    
    json_str = JSON::stringify(data)
    print(json_str)  // {"name":"Alice","role":"admin"}
    
    return 0
}

Time and Date

Current Time

wyn
fn main() -> int {
    now = Time::now()
    print("Current timestamp: ${now}")
    
    return 0
}

Formatting Time

wyn
fn main() -> int {
    now = Time::now()
    formatted = Time::format(now, "%Y-%m-%d %H:%M:%S")
    print(formatted)  // "2026-02-09 13:00:00"
    
    return 0
}

Time Arithmetic

Timestamps are seconds since the epoch-plain integers, so plain arithmetic works:

wyn
fn main() -> int {
    now = Time::now()
    tomorrow = now + 86400   // Add 24 hours (in seconds)
    yesterday = now - 86400
    print(tomorrow - yesterday)  // 172800
    
    return 0
}

Sleep

wyn
fn main() -> int {
    print("Starting...")
    sleep(1000)  // Sleep for 1000ms (1 second)
    print("Done!")
    
    return 0
}

System Operations

Command-Line Arguments

wyn
fn main() -> int {
    args = System::args()
    
    print("Program name: " + args[0])
    
    for (var i = 1; i < args.len(); i = i + 1) {
        print("Arg ${i}: ${args[i]}")
    }
    
    return 0
}

Environment Variables

Env::get returns the value, or an empty string if the variable isn't set:

wyn
fn main() -> int {
    home = Env::get("HOME")
    if (home.len() > 0) {
        print("Home: " + home)
    } else {
        print("HOME not set")
    }
    
    Env::set("MY_VAR", "my_value")
    
    return 0
}

Process Execution

System::exec returns the command's output; System::exec_code returns its exit code:

wyn
fn main() -> int {
    output = System::exec("ls -la")
    print("Output: " + output)
    
    code = System::exec_code("ls -la")
    print("Exit code: ${code}")
    
    return 0
}

Exit Codes

wyn
fn main() -> int {
    fatal = File::exists("panic.flag")
    if (fatal) {
        print("Fatal error")
        return 1  // Non-zero indicates error
    }
    
    return 0  // Success
}

Math Operations

Basic Math

wyn
fn main() -> int {
    x = 16.0
    
    print(Math::sqrt(x))        // 4.0
    print(Math::pow(2.0, 8.0))  // 256.0
    print(abs(-42))             // 42
    print(min(5, 3))            // 3
    print(max(5, 3))            // 5
    
    return 0
}

Trigonometry

wyn
fn main() -> int {
    print(Math::sin(Math::PI / 2.0))   // 1.0
    print(Math::cos(0.0))              // 1.0
    print(Math::tan(Math::PI / 4.0))   // 1.0
    
    return 0
}

Random Numbers

wyn
fn main() -> int {
    // Random integer between 0 and 99
    rand_int = random_int(0, 100)
    print(rand_int)
    
    // Random float between 0.0 and 1.0
    rand_float = random_float()
    print(rand_float)
    
    return 0
}

Putting It Together: Real Examples

Log File Parser

wyn
fn count_errors(path: string) -> Result<int, string> {
    if (not File::exists(path)) {
        return Err("file not found: " + path)
    }
    
    contents = File::read(path)
    lines = contents.split("\n")
    errors = 0
    
    for (var i = 0; i < lines.len(); i = i + 1) {
        if (lines[i].contains("ERROR")) {
            errors = errors + 1
        }
    }
    
    return Ok(errors)
}

fn main() -> int {
    result = count_errors("app.log")
    
    match result {
        Ok(count) => print("Errors: ${count}"),
        Err(msg) => print("Error: " + msg)
    }
    
    return 0
}

REST API Client

wyn
struct User {
    id: int,
    name: string,
    email: string
}

fn fetch_user(id: int) -> Result<User, string> {
    url = "https://api.example.com/users/${id}"
    body = Http.get(url)
    
    if (body.len() == 0) {
        return Err("request failed")
    }
    
    json = json_parse(body)
    
    user = User {
        id: json["id"],
        name: json["name"],
        email: json["email"]
    }
    
    return Ok(user)
}

fn main() -> int {
    result = fetch_user(1)
    
    match result {
        Ok(user) => {
            print("User: " + user.name)
            print("Email: " + user.email)
        },
        Err(msg) => print("Error: " + msg)
    }
    
    return 0
}

Configuration Manager

wyn
struct Config {
    host: string,
    port: int
}

fn load_config(path: string) -> Result<Config, string> {
    if (not File::exists(path)) {
        return Err("config file not found")
    }
    
    contents = File::read(path)
    json = json_parse(contents)
    
    config = Config {
        host: json["host"],
        port: json["port"]
    }
    
    return Ok(config)
}

fn save_config(path: string, config: Config) -> int {
    data = {
        "host": config.host,
        "port": config.port.to_string()
    }
    
    return File::write(path, JSON::stringify(data))
}

fn main() -> int {
    config = load_config("config.json")
    
    match config {
        Ok(cfg) => {
            print("Host: " + cfg.host)
            print("Port: ${cfg.port}")
        },
        Err(msg) => {
            print("Using defaults")
            default_cfg = Config {
                host: "localhost",
                port: 8080
            }
            save_config("config.json", default_cfg)
        }
    }
    
    return 0
}

What You've Learned

Wyn's standard library covers strings, files, networking, JSON, time, and system operations. It's enough to build real applications without external dependencies.

I/O primitives signal failure with simple values-empty strings, 0/1 status codes-so guard with checks like File::exists and wrap them in your own Result-returning functions for clean error handling with match and ?.

The library is designed for simplicity. Functions do one thing well. Types are straightforward. Error messages are clear.

For complete API documentation, see Appendix B.

Data Persistence

Save and load HashMaps as JSON files:

wyn
fn main() -> int {
    // Save state
    data = { "name": "Alice", "score": "100" }
    Data.save("state.json", data)

    // Load state (returns empty HashMap if file missing)
    loaded = Data.load("state.json")
    print(loaded.get("name"))   // Alice
    print(loaded.get("score"))  // 100
    return 0
}

Exercises

  1. File processor: Read a text file, count words, lines, and characters. Write results to another file.

  2. HTTP client: Fetch data from a public API, parse the JSON, and display specific fields.

  3. Log analyzer: Parse a log file, count different log levels (INFO, WARN, ERROR), and generate a summary.

  4. Config manager: Create a program that loads configuration from a JSON file, with fallback to defaults if the file doesn't exist.

  5. Simple web server: Create an HTTP server that serves static files from a directory.

  6. Command-line tool: Build a CLI tool that accepts arguments and performs different actions based on them.

  7. Data pipeline: Read CSV data, transform it, and write JSON output.


Next: Chapter 15: Real-World Applications

MIT License - v1.19.1