Skip to content

A compiled language for people who like Python

Feels like Python.Runs like C.Ships as one binary.

Wyn compiles to C and links a tiny runtime — no VM, no garbage collector. Memory is managed by reference counting, concurrency is spawn/await on coroutines, and a dependency is just a git URL.

struct Job {
    name: string
    ms: int

    fn report(self) -> string {
        return "${self.name} finished in ${self.ms}ms"
    }
}

fn run_job(name: string) -> int {
    return name.len() * 3   // stand-in for real work
}

fn main() {
    var f1 = spawn run_job("resize")
    var f2 = spawn run_job("transcode")

    var jobs = [
        Job{name: "resize", ms: await f1},
        Job{name: "transcode", ms: await f2},
    ]

    for j in jobs {
        println(j.report())
    }
}
$ wyn run jobs.wyn
Compiled in 392ms
resize finished in 18ms
transcode finished in 27ms

Real program, real output — compiled on an Apple M4 before publishing.

~0.4s
hello-world build
<1s
full rebuild
~5μs
spawn + await
49KB
hello-world binary
7,000
req/s · web pkg, c=200

Measured on Apple M4 — method and details on the benchmarks page.

Zero to running

Project, tests, binary. The terminal transcripts below are unedited.

wyn new
$ wyn new myapp --template cli
✓ Created cli project: myapp/
myapp/wyn.toml
myapp/src/main.wyn
myapp/tests/test_main.wyn
myapp/README.md
wyn test
$ cd myapp && wyn test
Scanning: tests/
✓ starts_with
✓ upper
✓ split
✓ file roundtrip
4 tests passed
wyn build
$ wyn build src/main.wyn
✓ Built: src/main (53KB, 336ms)
$ ./src/main
myapp v0.1.0
Usage: myapp <command> [options]

The language in five tabs

Every tab compiles as shown. The output pane is the program's actual output.

fn count_primes(lo: int, hi: int) -> int {
    var count = 0
    for n in lo..hi {
        if n < 2 { continue }
        var prime = true
        var d = 2
        while d * d <= n {
            if n % d == 0 { prime = false; break }
            d = d + 1
        }
        if prime { count = count + 1 }
    }
    return count
}

fn main() {
    // four cores, zero ceremony
    var a = spawn count_primes(0, 1000000)
    var b = spawn count_primes(1000000, 2000000)
    var c = spawn count_primes(2000000, 3000000)
    var d = spawn count_primes(3000000, 4000000)

    var total = await a + await b + await c + await d
    println("primes below 4M: ${total}")
}
$ wyn run example.wyn
primes below 4M: 283146
Edit in playground ↗

Everything is an object — chain anything

Strings, arrays, and lambda results all chain through methods. Each snippet compiles as shown; the output line is the program's actual output.

strings.wynedit ↗
result = "wyn, is, neat".split(",").map((s) => s.trim().upper()).join(" > ")
println(result)
$ wyn run strings.wyn
WYN > IS > NEAT
arrays.wynedit ↗
nums = [5, 3, 8, 1, 9, 2]
top = nums.sort().reverse().slice(0, 3).sum()
println(top.to_string())
$ wyn run arrays.wyn
22
pipeline.wynedit ↗
words = ["compiler", "is", "an", "object", "pipeline"]
long = words.filter((s) => s.len() > 2).map((s) => s.upper()).join(" ")
println(long)
$ wyn run pipeline.wyn
COMPILER OBJECT PIPELINE

No VM, no GC

Automatic reference counting frees memory deterministically at scope exit. No pauses, no tuning flags, no runtime to install on the target machine.

Concurrency built in

spawn f() starts a coroutine; await collects the result. Cooperative I/O under the hood, ~5μs per spawn+await round trip.

Packages are git repos

wyn add web resolves to github.com/wynlang/web. Any host works, wyn.lock pins exact commits. No registry, no account, no publish step.

C is one import away

wyn bind header.h generates FFI bindings from a C header — proven against SQLite, lz4, and zstd. Forty years of C libraries, available directly.

Tooling in the box

Formatter, test runner, REPL, doc generator, LSP for VS Code and Neovim. One binary installs all of it.

Cross-compile anywhere

Linux, macOS, Windows targets from one machine — plus iOS, Android, and WebAssembly builds from the same source.

A web server is a for loop

The official web package is a thin layer over Wyn's native HTTP: parse the request, respond with a helper, spawn a handler per connection. It sustains ~7,000 req/s at 200 concurrent connections with zero failed requests on an Apple M4 — with keep-alive work in flight to push that much higher.

$ wyn pkg add web
$ wyn run server.wyn
http://localhost:8080
server.wyn
import web

fn handle(conn: int) {
    var req = web.read_request(conn)
    if req.len() == 0 { return }
    if web.is(req, "GET", "/") == 1 {
        web.html(req, 200,
            web.page("Hello", "<h1>Hello from Wyn</h1>", ""))
    } else {
        web.not_found(req)
    }
}

fn main() {
    var server = web.listen(8080)
    println("http://localhost:8080")
    while true {
        var conn = web.accept(server)
        if conn > 0 { spawn handle(conn) }
    }
}

Where Wyn stands

An honest snapshot — including the rows we lose.

TraitWynGoRustPython
Native binary, no runtime install
No garbage collector
Sub-second builds
List comprehensions, slices, in
Lightweight spawn/await tasks
Generators with yield
Mature ecosystem
Production track recordearly

Meet Wynter

Every Wyn install ships with a wyvern in the error messages. Install it, break something, and Wynter will point you at the line.

MIT License — v1.18