Skip to content

Wyn v1.13: The Pythonic Release

v1.12 was about trust. v1.13 is about feel.

Wyn already reads a lot like Python — for, list comprehensions, string interpolation. v1.13 closes most of the remaining gaps in the everyday syntax: membership tests, negative indices, open-ended slices, map iteration, and tuple unpacking. If you've written Python, you already know how to write these.

It's all backward compatible. No source changes are required to upgrade.

in / not in

The one every Python developer reaches for on day one. It dispatches on the container — array element, substring, or map key:

wyn
var nums = [1, 2, 3]
if 2 in nums { println("has 2") }
if 5 not in nums { println("no 5") }

if "ell" in "hello" { println("substring") }

var config = {"debug": "on"}
if "debug" in config { println("debug is set") }

It sits at comparison precedence, so it reads cleanly inside if and while.

Negative indexing and open-ended slices

a[-1] is the last element, and slice bounds are optional:

wyn
var a = [10, 20, 30, 40, 50]

println(a[-1].to_string())   // 50 — last
println(a[-2].to_string())   // 40 — second to last

var head = a[:2]             // [10, 20]      — first two
var tail = a[2:]             // [30, 40, 50]  — from index 2
var copy = a[:]              // full copy

Slices work on strings too:

wyn
var s = "hello world"
println(s[:5])               // "hello"
println(s[6:])               // "world"

Map iteration

Iterate a map directly, binding key and value — no more keys() dance:

wyn
var scores = {"alice": 90, "bob": 85}

for name, score in scores {
    println("${name}: ${score}")
}

for name in scores {
    println(name)
}

And map values keep their real type, so scores["alice"] gives you back an int, not a stringified one.

Tuple unpacking and swap

Declare several variables at once, and swap without a temporary:

wyn
var a, b = 10, 20

fn divmod(a: int, b: int) -> (int, int) {
    return (a / b, a % b)
}
var q, r = divmod(17, 5)     // q = 3, r = 2

a, b = b, a                  // swap — RHS evaluated before any write

The right-hand side is fully evaluated before anything is written, so rotations (x, y, z = z, x, y) work exactly the way you'd expect.

Some / None / Ok / Err, anywhere

The optional and result constructors now work with any payload type in any context — including a plain annotated variable, not just a function return. This used to fail to compile:

wyn
var name: string? = Some("Wyn")
var missing: string? = None()
var count: int? = Some(42)

Optionals carry convenience methods too, so you don't always need a full match:

wyn
var name: string? = Some("Wyn")
if name.is_some() {
    println("name = ${name.unwrap()}")
}

var count: int? = None()
println(count.unwrap_or(0).to_string())   // 0

Under the hood

Getting these features right meant fixing a cluster of related bugs: HashMap values of any type now round-trip through m[k] and m.get(k), float and bool arrays store their real values, and match on enums with payloads and multi-field variants works correctly.

Upgrading

bash
wyn --version        # 1.13.0

No source changes are required. v1.13.0 is a drop-in upgrade over v1.12.0.


Read the full what's new in v1.13, or browse the version history.

MIT License — v1.13