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:
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:
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 copySlices work on strings too:
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:
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:
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 writeThe 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:
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:
var name: string? = Some("Wyn")
if name.is_some() {
println("name = ${name.unwrap()}")
}
var count: int? = None()
println(count.unwrap_or(0).to_string()) // 0Under 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
wyn --version # 1.13.0No 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.