Traits
Part of: Reef Language Reference Last reviewed on version: 0.9.0 Status: Implemented
Overview
Traits define abstract interfaces that types can implement. They enable:
- Polymorphism through shared interfaces
- Generic constraints (bounds)
- Code organization and API design
Key Features:
- Trait definitions with abstract method signatures
- Implementation blocks for any named type (structs, Active Objects, objects, enums)
- Generic constraints using
where T: TraitName - Multiple trait implementations per type
Trait Definitions
Basic Syntax
Define a trait with trait Name ... end Name:
trait Printable
proc print();
end Printable
The semicolon after method signatures indicates abstract methods (no body).
A method with a body is a type error in 0.9 (Trait default method bodies are not supported); every impl must provide every method.
Default implementations (clone-per-impl) are deferred:
reef/reef-lang#5.
Multiple Methods
Traits can require multiple methods:
trait Comparable
fn compare(other: int): int;
fn equals(other: int): bool;
end Comparable
Method Signatures
Methods can be procedures (no return) or functions (with return):
trait Serializable
fn serialize(): string; // Function - returns value
proc deserialize(s: string); // Procedure - no return
end Serializable
Implementing Traits
Basic Implementation
Use impl TraitName for Type ... end impl:
type Point = struct
x: int
y: int
end Point
impl Printable for Point
proc print()
println("Point")
end print
end impl
Multiple Implementations
A type can implement multiple traits:
type Value = struct
v: int
end Value
impl Printable for Value
proc print()
println("Value")
end print
end impl
impl Comparable for Value
fn compare(other: int): int
self.v - other
end compare
fn equals(other: int): bool
self.v == other
end equals
end impl
Note: an impl block must provide every method the trait declares — the
Comparable trait above requires both compare() and equals().
A type's own methods and its trait-impl methods share one name space: two bodies for the same name (an own method plus an impl method, or two impls providing the same method) are a type error. An own method satisfying a trait so the impl may omit it is not in 0.9.
Objects
impl Trait for Class is legal. Subclasses inherit the base's impls for
method syntax (button.draw() finds impl Trait for Widget). Trait
methods are statically dispatched to that impl target — they are not
virtual. The pattern that does vary per subclass is delegate to a
virtual: the class method is virtual, the impl forwards under a
different name. Same-name own method plus impl is still the error
above. Full example and the where T: Trait subclass case:
040_OBJECTS.md. The
examples/widget_tree.reef showcase runs the pattern on a small tree.
Self Access
Inside trait implementations, use self to access the implementing type's fields:
impl Comparable for Point
fn compare(other: int): int
self.x - other
end compare
fn equals(other: int): bool
self.x == other
end equals
end impl
Generic Constraints
Where Clauses
Constrain generic type parameters with where T: TraitName:
fn describe[T](item: T): int where T: Printable
// T must implement Printable
42
end describe
Using Constrained Functions
Call with types that implement the required trait:
proc main()
let p = Point { x: 1, y: 2 }
let v = Value { v: 100 }
// Both Point and Value implement Printable
let r1 = describe(p) // OK
let r2 = describe(v) // OK
end main
API Reference
Trait Definition
trait TraitName
fn method_name(params...): return_type; // Required function
proc method_name(params...); // Required procedure
end TraitName
Trait Implementation
impl TraitName for Type
fn method_name(params...): return_type
// Implementation body
end method_name
proc method_name(params...)
// Implementation body
end method_name
end impl
Generic Constraint
fn function_name[T](param: T): return_type where T: TraitName
// Body - T guaranteed to implement TraitName
end function_name
Current Limitations
Note: Trait constraint checking is implemented, but dynamic dispatch (calling trait methods through a trait reference) is not yet supported. Current usage:
- Define traits and implementations for type documentation
- Use where clauses to constrain generic functions
- Call methods directly on concrete types
Future versions will add:
- Dynamic dispatch through trait objects
- Default method implementations (clone-per-impl: Isurus reef/reef-lang#5; 0.9 rejects a body on a trait method rather than silently ignoring it)
- Multiple constraint bounds (
where T: A + B)
Examples
Complete Example
// Define traits
trait Printable
proc print();
end Printable
trait Comparable
fn equals(other: int): bool;
end Comparable
// Define type
type Point = struct
x: int
y: int
end Point
// Implement traits
impl Printable for Point
proc print()
println("Point")
end print
end impl
// Generic function with constraint
fn describe[T](item: T): int where T: Printable
42
end describe
proc main()
let p = Point { x: 1, y: 2 }
let result = describe(p)
println("Done!")
end main