polymorphism
Traits, enums, and generics — Sema polymorphism without classes or inheritance.
Run it from sema/:
sema check examples/polymorphismSEMA_STRICT=1 sema run examples/polymorphismsema assure examples/polymorphism --grade silverSource
Section titled “Source”src/main.sema
Section titled “src/main.sema”"""Polymorphism worked example (§3.9).
Demonstrates the whole trait trio and a functional pipeline in one runnableprogram on the deterministic mock engine:
- a **struct conforming via the header list** (`Version`),- a **struct conforming out of line** with `impl Ord for Money` (retrofit),- an **enum conforming to the same trait** (`Severity`), defaults grafted too,- a **bounded generic** `maximum[T: Ord]` reused across all three,- **default methods** (`less`/`max2`/`clamp`) derived from one `compare`,- a **supertrait** obligation (`Ord` requires `Eq`),- **trait objects**: a heterogeneous `list[Renderer]` dispatched dynamically, with `is` narrowing (open-world polymorphism — see `plugins.sema`),- **functional style**: immutable bindings, a comprehension, conditional expressions."""
assure gold
from polymorphism.order import Ord, maximumfrom polymorphism.plugins import Renderer, Text, Bullet, Rule, render_all, count_rules
struct Version (Ord): """Ordered by major, then minor.""" major: int minor: int def compare(self, other: Version) -> int !{}: return (self.major - other.major) if self.major != other.major else (self.minor - other.minor)
# Out-of-line conformance: `Money` is declared plainly, then retrofitted to# `Ord` — the same defaults graft on as if listed in the header.struct Money: minor_units: int
impl Ord for Money: def compare(self, other: Money) -> int !{}: return self.minor_units - other.minor_units
# An enum conforms to the very same trait; `less`/`max2`/`clamp` graft onto it.enum Severity (Ord): low medium high def rank(self) -> int !{}: return 0 if self == Severity.low else (1 if self == Severity.medium else 2) def compare(self, other: Severity) -> int !{}: return self.rank() - other.rank()
def latest_version() -> Version !{}: ensure result.major == 2 ensure result.minor == 0 return maximum([Version(major=1, minor=4), Version(major=2, minor=0), Version(major=1, minor=9)])
def maximum_money(prices: list[Money]) -> Money !{}: require len(prices) > 0 return maximum(prices)
def highest_severity() -> Severity !{}: ensure result == Severity.high return maximum([Severity.low, Severity.high, Severity.medium])
def clamped_version(value: Version, lower: Version, upper: Version) -> Version !{}: return value.clamp(lower, upper)
def rendered_report() -> str !{}: ensure result == "Report|----|- alpha|- beta" return render_all([Text(body="Report"), Rule(width=4), Bullet(item="alpha"), Bullet(item="beta")])
def rendered_rule_count(items: list[Renderer]) -> int !{}: return count_rules(items)
def default_methods_hold() -> bool !{}: ensure result == true lower = Version(major=1, minor=9) upper = Version(major=2, minor=0) return lower.less(upper) and upper.eq(Version(major=2, minor=0))
test "bounded generic selects latest Version 2.0": latest = latest_version() ensure latest.major == 2 ensure latest.minor == 0
test "out-of-line Money impl selects maximum 1799": prices = [Money(minor_units=1299), Money(minor_units=999), Money(minor_units=1799)] ensure maximum_money(prices).minor_units == 1799
test "bounded generic preserves a maximum in the first slot": prices = [Money(minor_units=1799), Money(minor_units=1299), Money(minor_units=999)] ensure maximum(prices).minor_units == 1799
test "enum Ord dispatch selects Severity.high": ensure highest_severity() == Severity.high
test "enum rank distinguishes every variant": ensure Severity.low.rank() == 0 ensure Severity.medium.rank() == 1 ensure Severity.high.rank() == 2
test "default clamp returns Version 2.0": clamped = clamped_version(Version(major=5, minor=0), Version(major=1, minor=0), Version(major=2, minor=0)) ensure clamped.major == 2 ensure clamped.minor == 0
test "trait-object dispatch renders Report": ensure rendered_report() == "Report|----|- alpha|- beta"
test "is narrowing counts one Rule": items = [Text(body="Report"), Rule(width=4), Bullet(item="alpha"), Bullet(item="beta")] ensure rendered_rule_count(items) == 1
test "Ord defaults provide less and eq": ensure default_methods_hold() == true
def main() -> str !{}: ensure result == "latest=2.0 dearest=1799 cheaper=[1299, 999] top=high clamped=2.0 rendered=Report|----|- alpha|- beta rules=1" # Bounded generic over a user struct. latest = latest_version()
# Retrofitted struct + a functional comprehension using a default method. prices = [Money(minor_units=1299), Money(minor_units=999), Money(minor_units=1799)] dearest = maximum_money(prices) cheaper = [p.minor_units for p in prices if p.less(dearest)]
# Enum ordering through the grafted defaults. top = highest_severity()
# `clamp` is a default method that calls other defaults. clamped = clamped_version(latest, latest, latest)
# Trait objects: heterogeneous lists dispatch dynamically inside the wrappers. rendered = rendered_report() rules = rendered_rule_count([Rule(width=len(rendered))])
summary = f"latest={latest.major}.{latest.minor} dearest={dearest.minor_units} cheaper={cheaper} top={top.name} clamped={clamped.major}.{clamped.minor} rendered={rendered} rules={rules}" print(summary) return summarysrc/order.sema
Section titled “src/order.sema”"""Reusable ordering vocabulary (§3.9).
`Ord` is built on the supertrait `Eq`. A conforming type supplies a singlerequired method — `compare` — and inherits every other operation as a *defaultmethod*: `less`, `eq`, `max2`, and `clamp` are written once here and graftedonto every type that conforms, with the type's own definition winning if itprovides one. `maximum` is a *bounded generic*: it works for any `T` that is`Ord`, using only the trait's surface."""
pub trait Eq: """Equality by value.""" def eq(self, other: Self) -> bool !{}
pub trait Ord (Eq): """A total order. Supply `compare`; the rest is provided for free.""" def compare(self, other: Self) -> int !{}
# --- default (provided) methods: written once, reused by every conformer --- def less(self, other: Self) -> bool !{}: return self.compare(other) < 0
def eq(self, other: Self) -> bool !{}: return self.compare(other) == 0
def max2(self, other: Self) -> Self !{}: return other if self.less(other) else self
def clamp(self, lo: Self, hi: Self) -> Self !{}: return lo if self.less(lo) else (hi if hi.less(self) else self)
# Bounded generic (§3.9): `[T: Ord]` is erased at runtime, but the bound is the# declared obligation that the element type provides `Ord`, so the body may use# `max2`. Works uniformly for structs and enums that conform.pub def maximum[T: Ord](xs: list[T]) -> T !{}: require len(xs) > 0 mut best = xs[0] for x in xs[1:]: best = best.max2(x) return bestsrc/plugins.sema
Section titled “src/plugins.sema”"""Trait objects (§3.9) — open-world polymorphism.
A `Renderer` trait with several unrelated concrete implementations, heldtogether in one `list[Renderer]` and dispatched dynamically. This is the patternclasses use *inheritance* for (a heterogeneous collection behind an interface),done with traits + dynamic dispatch instead — third parties can add new`Renderer`s without touching a central `enum`. `is` recovers the concrete typewhen open-world code needs it."""
pub trait Renderer: """Anything that can render itself to a line of text.""" def render(self) -> str
pub struct Text (Renderer): body: str def render(self) -> str !{}: return self.body
pub struct Bullet (Renderer): item: str def render(self) -> str !{}: return f"- {self.item}"
pub struct Rule (Renderer): width: int def render(self) -> str !{}: return "-" * self.width
# A heterogeneous collection behind the trait, dispatched dynamically: each# element is a different concrete type, resolved at the call site by its runtime# type. No `enum`, no shared base class.pub def render_all(items: list[Renderer]) -> str !{}: lines = [it.render() for it in items] return "|".join(lines)
# `is` narrowing: open-world code can still ask a value's concrete type or test# trait conformance.pub def count_rules(items: list[Renderer]) -> int !{}: mut n = 0 for it in items: n = n + (1 if it is Rule else 0) return nReflected API
Section titled “Reflected API”Polymorphism worked example (§3.9).
Demonstrates the whole trait trio and a functional pipeline in one runnable program on the deterministic mock engine:
- a struct conforming via the header list (
Version), - a struct conforming out of line with
impl Ord for Money(retrofit), - an enum conforming to the same trait (
Severity), defaults grafted too, - a bounded generic
maximum[T: Ord]reused across all three, - default methods (
less/max2/clamp) derived from onecompare, - a supertrait obligation (
OrdrequiresEq), - trait objects: a heterogeneous
list[Renderer]dispatched dynamically, withisnarrowing (open-world polymorphism — seeplugins.sema), - functional style: immutable bindings, a comprehension, conditional expressions.
struct Version
Section titled “struct Version”Ordered by major, then minor.
Fields
| field | type | descriptor |
|---|---|---|
major |
int |
|
minor |
int |
struct Money
Section titled “struct Money”Fields
| field | type | descriptor |
|---|---|---|
minor_units |
int |
enum Severity
Section titled “enum Severity”Variants
lowmediumhigh
def latest_version
Section titled “def latest_version”def latest_version() -> Version !{}Returns Version
Effects !{}
def maximum_money
Section titled “def maximum_money”def maximum_money(prices: list[Money]) -> Money !{}Parameters
| name | type |
|---|---|
prices |
list[Money] |
Returns Money
Effects !{}
def highest_severity
Section titled “def highest_severity”def highest_severity() -> Severity !{}Returns Severity
Effects !{}
def clamped_version
Section titled “def clamped_version”def clamped_version(value: Version, lower: Version, upper: Version) -> Version !{}Parameters
| name | type |
|---|---|
value |
Version |
lower |
Version |
upper |
Version |
Returns Version
Effects !{}
def rendered_report
Section titled “def rendered_report”def rendered_report() -> str !{}Returns str
Effects !{}
def rendered_rule_count
Section titled “def rendered_rule_count”def rendered_rule_count(items: list[Renderer]) -> int !{}Parameters
| name | type |
|---|---|
items |
list[Renderer] |
Returns int
Effects !{}
def default_methods_hold
Section titled “def default_methods_hold”def default_methods_hold() -> bool !{}Returns bool
Effects !{}
def main
Section titled “def main”def main() -> str !{}Returns str
Effects !{}
Reusable ordering vocabulary (§3.9).
Ord is built on the supertrait Eq. A conforming type supplies a single
required method — compare — and inherits every other operation as a default
method: less, eq, max2, and clamp are written once here and grafted
onto every type that conforms, with the type’s own definition winning if it
provides one. maximum is a bounded generic: it works for any T that is
Ord, using only the trait’s surface.
trait Eq
Section titled “trait Eq”Equality by value.
trait Ord
Section titled “trait Ord”A total order. Supply compare; the rest is provided for free.
def maximum
Section titled “def maximum”def maximum[T: Ord](xs: list[T]) -> T !{}Parameters
| name | type |
|---|---|
xs |
list[T] |
Returns T
Effects !{}
plugins
Section titled “plugins”Trait objects (§3.9) — open-world polymorphism.
A Renderer trait with several unrelated concrete implementations, held
together in one list[Renderer] and dispatched dynamically. This is the pattern
classes use inheritance for (a heterogeneous collection behind an interface),
done with traits + dynamic dispatch instead — third parties can add new
Renderers without touching a central enum. is recovers the concrete type
when open-world code needs it.
trait Renderer
Section titled “trait Renderer”Anything that can render itself to a line of text.
struct Text
Section titled “struct Text”Fields
| field | type | descriptor |
|---|---|---|
body |
str |
struct Bullet
Section titled “struct Bullet”Fields
| field | type | descriptor |
|---|---|---|
item |
str |
struct Rule
Section titled “struct Rule”Fields
| field | type | descriptor |
|---|---|---|
width |
int |
def render_all
Section titled “def render_all”def render_all(items: list[Renderer]) -> str !{}Parameters
| name | type |
|---|---|
items |
list[Renderer] |
Returns str
Effects !{}
def count_rules
Section titled “def count_rules”def count_rules(items: list[Renderer]) -> int !{}Parameters
| name | type |
|---|---|
items |
list[Renderer] |
Returns int
Effects !{}