Traits
Traits define shared behavior; impls provide it per type. NG supports default methods, supertraits, generic impls, auto traits, derive, trait bounds, and dynamic ref<Trait> views.
Defining a trait
Methods take the receiver as self: Self ref (or Self ref mut):
trait Show {
fun show(self: Self ref) -> string;
}Default methods may call the trait's own methods through self:
trait Show {
fun show(self: Self ref) -> string;
fun bracketed(self: Self ref) -> string {
return "[" + self.show() + "]";
}
}Impls
impl Show for Counter {
fun show(self: Self ref) -> string {
return (*self).label;
}
}An empty impl uses the trait's defaults:
impl Display for Number { }Impls are coherent: duplicates are rejected, and unknown or missing methods (without defaults) are errors.
Generic impls
impl<T> Trait for Type<T> matches concrete receivers per call:
impl<T> Show for List<T> {
fun show(self: Self ref) -> string { return "list"; }
}
impl<T> Show for array<T> {
fun show(self: Self ref) -> string { return "array"; }
}Concrete impls take precedence over generic ones; each call site instantiates the impl's methods per concrete receiver.
Supertraits
trait Ord: Eq {
fun less(self: Self ref, other: Self ref) -> bool;
}Implementing Ord provides the Eq methods too.
Qualified calls
let text = Show.show(42); // qualified
let text = 42.show(); // method syntaxTrait bounds
fun render<U: Show>(value: U ref) -> string {
return value.show();
}Bounds appear on generic parameters or in where clauses; impl evidence is checked per instance (abstract calls defer to monomorphization).
Auto traits and derive
auto trait Send {}Auto traits are implemented for every concrete type implicitly.
derive(Copy + Clone) on a struct synthesizes the impls, including a deep-copying clone() method:
struct Point: derive(Copy + Clone) {
x: i64,
y: i64,
}Dynamic views: ref<Trait>
ref<Trait> is a shared-borrowed view over any implementing value, dispatched through per-concrete tables:
fun render(item: ref<Show>) -> string {
return item.show();
}
let counter = Counter { label: "seven" };
let view: ref<Show> = counter;
let text = view.show(); // dynamic dispatchViews coerce from bindings, call arguments, and array elements; default methods dispatch through views too. Bare trait values (let x: Show) are rejected — ref<Show> is the only dynamic form.
Next: Type System in Depth.