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:

  1. Define traits and implementations for type documentation
  2. Use where clauses to constrain generic functions
  3. 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