Skip to content

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 in membership on arrays, strings, and maps.
  • Negative indexinga[-1] is the last element.
  • Open-ended slicesa[:2], a[2:], a[:].
  • Map iterationfor k, v in map and for k in map.
  • Tuple unpacking & swapvar a, b = 1, 2 and a, b = b, a.
  • Some / None / Ok / Err for 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:

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") }

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:

wyn
var a = [10, 20, 30, 40]
println(a[-1].to_string())   // 40 — last
println(a[-2].to_string())   // 30 — second to last

Open-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:

wyn
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 too

Slices also work on strings:

wyn
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:

wyn
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:

wyn
var scores = {"alice": 90, "bob": 85}
println(scores["alice"].to_string())   // 90

Tuple Unpacking and Swap

Declare several variables at once from a tuple literal or a tuple-returning function:

wyn
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 = 2

The right-hand side is evaluated in full before anything is written, so swap and rotate need no temporary:

wyn
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 = 2

Some / 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:

wyn
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:

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 — fallback when None

Correctness Fixes

Several related bugs were fixed along the way:

  • HashMap values of any type work through m[k] and m.get(k).
  • Float and bool arrays store and return their real values.
  • match on enums with payloads and multi-field variants works.

Upgrading

bash
wyn --version        # 1.13.0

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

See also

MIT License — v1.13