What's New in v1.13
"The Pythonic Release" — the everyday syntax you already know from Python, now in a language that compiles to a native binary.
If you've written Python, most of this will feel like muscle memory: x in list, a[-1], a[:2], for k, v in map, a, b = b, a. It's all backward compatible — no source changes are required to upgrade.
Highlights
in/not inmembership on arrays, strings, and maps.- Negative indexing —
a[-1]is the last element. - Open-ended slices —
a[:2],a[2:],a[:]. - Map iteration —
for k, v in mapandfor k in map. - Tuple unpacking & swap —
var a, b = 1, 2anda, b = b, a. Some/None/Ok/Errfor any payload type, in any context.
Membership: in / not in
Test membership with in and not in. 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") }in sits at comparison precedence, so it reads naturally in if and while conditions. The in of a for … in … loop is unaffected.
Negative Indexing
Index from the end with a negative number:
var a = [10, 20, 30, 40]
println(a[-1].to_string()) // 40 — last
println(a[-2].to_string()) // 30 — second to lastOpen-Ended Slices
Slice with a[i:j], and omit either bound. a[:j] starts at the beginning, a[i:] runs to the end, and a[:] copies:
var a = [10, 20, 30, 40, 50]
var head = a[:2] // [10, 20] — first two
var tail = a[2:] // [30, 40, 50] — from index 2
var mid = a[1:4] // [20, 30, 40]
var copy = a[:] // full copy
println(tail[-1].to_string()) // 50 — negative index works on slices tooSlices also work on strings:
var s = "hello world"
println(s[:5]) // "hello"
println(s[6:]) // "world"Map Iteration
Iterate a HashMap directly. for k, v in map binds both key and value; for k in map iterates the keys:
var scores = {"alice": 90, "bob": 85}
for name, score in scores {
println("${name}: ${score}")
}
for name in scores {
println(name)
}Values keep their real type — a map of ints gives you ints back:
var scores = {"alice": 90, "bob": 85}
println(scores["alice"].to_string()) // 90Tuple Unpacking and Swap
Declare several variables at once from a tuple literal or a tuple-returning function:
var a, b = 10, 20 // a = 10, b = 20
fn divmod(a: int, b: int) -> (int, int) {
return (a / b, a % b)
}
var q, r = divmod(17, 5) // q = 3, r = 2The right-hand side is evaluated in full before anything is written, so swap and rotate need no temporary:
var a = 1
var b = 2
a, b = b, a // a = 2, b = 1
var x = 1
var y = 2
var z = 3
x, y, z = z, x, y // x = 3, y = 1, z = 2Some / None / Ok / Err for Any Type
The surface constructors now lower to the correct Option/Result for any payload type, in any context — not just a function return. Previously, a bare var o: string? = Some(s) failed to compile:
var name: string? = Some("Wyn")
var missing: string? = None()
var count: int? = Some(42)Optionals also carry convenience methods, so a full match isn't always needed:
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 — fallback when NoneCorrectness Fixes
Several related bugs were fixed along the way:
HashMapvalues of any type work throughm[k]andm.get(k).- Float and bool arrays store and return their real values.
matchon enums with payloads and multi-field variants works.
Upgrading
wyn --version # 1.13.0No source changes are required — v1.13.0 is a drop-in upgrade over v1.12.0.
See also
- What's New in v1.12 — the Hardening Release
- What's New in v1.11 — developer experience
- Version History