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.