Polymorphism and Where Clauses
This example starts with duplicate, a function that works with any type, then adds functions that need a to_str method. It shows how a where clause expresses that requirement and how a reusable where alias avoids repeating it.
duplicate : a -> (a, a) duplicate = |value| (value, value) show : a -> Str where [a.to_str : a -> Str] show = |value| value.to_str() a.Showable : where [a.to_str : a -> Str] show_pair : a, a -> Str where [a.Showable] show_pair = |left, right| "${left.to_str()}, ${right.to_str()}" expect duplicate(7.U8) == (7, 7) expect duplicate("Roc") == ("Roc", "Roc") expect show(42.U64) == "42" expect show_pair(2.I64, 3.I64) == "2, 3"
duplicate does not need to know anything about its argument’s type: it simply returns the value twice. By contrast, show calls to_str, so its annotation requires the argument’s type to provide that method.
a.Showable gives that requirement a name that show_pair can reuse. A type satisfies the alias by providing the required methods; there is no separate implementation declaration to write.
Output
Run this from the directory that has main.roc in it:
$ roc test main.roc All (4) tests passed in 7.2 ms.
Validated with Roc nightly-2026-10-29-a0a9fc2.