// E# — a verified example from the E# language corpus (CLR language; .es, not ECMAScript). // provenance: promotion_and_match.es topic: choice status: verified // hand-authored, idiomatic E# — verified through the E# compiler namespace Demo // ═════════════════════════════════════════════════════════════════════════════ // E#'s defining move: behavior lives in FREE FUNCTIONS, but a free function whose // first parameter is a value `data` is PROMOTED to a method on that type. You write // plain procedural code; you get to call it object-style — including chaining. // // func add(v: Vec, o: Vec) -> Vec is the method Vec.add → v.add(o) // // The free-call spelling `add(v, o)` is then a hard error (the compiler points you at // `v.add(o)`). The method travels with its receiver type, so it is reachable wherever // `Vec` is, regardless of which file declared the function. // // This file pairs that with the other half of E#'s type story — a `choice` (a closed // set of shapes) consumed by `match` (exhaustive pattern dispatch). A `choice` is NOT // a `data`, so functions over it are ordinary free functions, never promoted. // ═════════════════════════════════════════════════════════════════════════════ // A 2-D vector. A `data` is value-semantic: copying it copies the fields, and a // function that returns a new `Vec` never disturbs its input. data Vec { x: int y: int } // Each of these has `Vec` as its first parameter, so each is promoted onto `Vec`. // Call them `a.add(b)`, `a.scaled(2)`, `a.dot(b)` — never `add(a, b)`. func add(v: Vec, o: Vec) -> Vec { return Vec { x: v.x + o.x, y: v.y + o.y } } func scaled(v: Vec, k: int) -> Vec { return Vec { x: v.x * k, y: v.y * k } } func dot(v: Vec, o: Vec) -> int { return v.x * o.x + v.y * o.y } // Because `add` and `scaled` return a `Vec`, promoted calls CHAIN: each result is a // fresh value you can call the next method on. No mutation, no aliasing — just values // flowing through transformations. func combine(a: Vec, b: Vec) -> Vec { return a.add(b).scaled(2) } // The other half of the type story: a sum type. Each variant may carry payload fields. choice Shape { point // no payload segment(length: int) // one payload box(w: int, h: int) // two payloads } // `Shape` is a `choice`, not a `data`, so this is a plain free function — call it // `area(s)`. `match` must cover every variant (the compiler warns otherwise); each // arm binds that variant's payloads positionally. func area(s: Shape) -> int { match s { .point { return 0 } .segment(len) { return 0 } // a 1-D segment has no area .box(w, h) { return w * h } } return 0 } func main() -> int { let a = Vec { x: 1, y: 2 } let b = Vec { x: 3, y: 4 } let c = a.combine(b) // (a + b) then *2 → Vec { x: 8, y: 12 } — promoted, so `a.combine(b)` let d = a.dot(b) // 1*3 + 2*4 = 11 // Construct a choice value with its factory, then fold it with `match`. let boxArea = area(Shape.box(3, 5)) // 15 return c.x + c.y + d + boxArea // 8 + 12 + 11 + 15 = 46 }