Skip to content

Traits, enums, and generics — Sema polymorphism without classes or inheritance.

Run it from sema/:

Terminal window
sema check examples/polymorphism
SEMA_STRICT=1 sema run examples/polymorphism
sema assure examples/polymorphism --grade silver
"""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 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."""
from polymorphism.order import Ord, maximum
from polymorphism.plugins import 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 main() -> str !{}:
# Bounded generic over a user struct.
versions = [Version(major=1, minor=4), Version(major=2, minor=0), Version(major=1, minor=9)]
latest = maximum(versions)
# Retrofitted struct + a functional comprehension using a default method.
prices = [Money(minor_units=1299), Money(minor_units=999), Money(minor_units=1799)]
dearest = maximum(prices)
cheaper = [p.minor_units for p in prices if p.less(dearest)]
# Enum ordering through the grafted defaults.
top = maximum([Severity.low, Severity.high, Severity.medium])
# `clamp` is a default method that calls other defaults.
clamped = Version(major=5, minor=0).clamp(Version(major=1, minor=0), latest)
# Trait objects: one list, three concrete types, dynamic dispatch + `is`.
doc = [Text(body="Report"), Rule(width=4), Bullet(item="alpha"), Bullet(item="beta")]
rendered = render_all(doc)
rules = count_rules(doc)
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 summary
"""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."""
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 !{}:
mut best = xs[0]
for x in xs:
best = best.max2(x)
return best
"""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
`Renderer`s without touching a central `enum`. `is` recovers the concrete type
when 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 n

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 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.

Ordered by major, then minor.

Fields

fieldtypedescriptor
majorint
minorint

Fields

fieldtypedescriptor
minor_unitsint

Variants

  • low
  • medium
  • high
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.

Equality by value.

A total order. Supply compare; the rest is provided for free.

def maximum[T: Ord](xs: list[T]) -> T !{}

Parameters

nametype
xslist[T]

Returns T

Effects !{}

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.

Anything that can render itself to a line of text.

Fields

fieldtypedescriptor
bodystr

Fields

fieldtypedescriptor
itemstr

Fields

fieldtypedescriptor
widthint
def render_all(items: list[Renderer]) -> str !{}

Parameters

nametype
itemslist[Renderer]

Returns str

Effects !{}

def count_rules(items: list[Renderer]) -> int !{}

Parameters

nametype
itemslist[Renderer]

Returns int

Effects !{}