Traits
Traits describe the abilities of a type.
For example,
they can be used to specify that a type can be compared for order
using operators such as < and >= via the Ordering trait,
or that a value can be formatted as a string using the Debug trait.
Declaring a trait
Trait declarations are only allowed at the module level.
A trait is declared using the trait keyword.
The body can declare function types that an implementation of the trait must declare,
and optionally, it can declare fallback functions.
/// Format a value in a fancy way.
trait Fancify
// This must be defined by the implementation.
with function to_boring_string(value: Self) -> String
// This may be defined by the implementation. If not defined, the
// implementation will use the definition provided here as the fallback.
with function fancify(value: Self) -> String =
"✨ " + to_boring_string(value) + " ✨"
The functions that are defined for the trait exist in the module-level scope.
Self is a special value that is a placeholder for the type that implements the trait.
Implementing a trait
import std/fmt
implement Fancify for Int
with function to_boring_string(value: Int) -> String = fmt.display(value)
function example() -> String = fancify(123) // "✨ 123 ✨"
The fancify function with a default implementation can be overridden.
implement Fancify for Int
with function to_boring_string(value: Int) -> String = fmt.display(value)
with function fancify(value: Int) -> String =
"🍄 " + to_boring_string(value) + " 🍄"
function example() -> String = fancify(123) // "🍄 123 🍄"
Fuyu never lets trait be implemented twice for the same type, which would be ambiguous. Fuyu only lets you declare a trait implementation if either the trait is declared in your module or the type is declared in you module. This allows Fuyu to detect overlapping trait implementations only by checking in the current module!
Trait bounds
A trait can require that one or more other traits are implemented for the type.
For example, our above Fancify trait could be written as:
import std/fmt
trait Fancify: fmt.Display
with function fancify(value: Self) -> String = "✨ " + fmt.display(value) + " ✨"
Now an implementation of Fancify can be provided for any type which has the Display trait,
such as Int.
implement Fancify for Int
function example() -> String = fancify(123) // "✨ 123 ✨"
A more generalized version can be written that applies to any type that has the Display trait.
This means that unless a different implementation is required,
all types that implement Display can be used with fancify().
implement Fancify for a where a: fmt.Display
function example() -> String = fancify(123.456) // "✨ 123.456 ✨"
Multiple trait bounds on a type are expressed with +,
such as a: fmt.Display + ops.Add.
For example, a trait bound on a function:
function sum_stringify_repeat(first: a, second: a) -> String
where a: fmt.Display + ops.Add =
fmt.display(first + second)
Trait bounds for multiple variables are separated by , in the where clause:
function initial(first: a, second: b) -> (a, b)
where a: Default, b: Default =
(default(), default())
function example() -> (Int, String) = initial() // (0, "")