Traits
Traits define behavioral contracts that types can implement. They work with for blocks to ensure types provide specific functionality.
Defining a Trait
Section titled “Defining a Trait”A trait declares method signatures that implementing types must provide:
trait Display { let display(self) -> string}Implementing a Trait
Section titled “Implementing a Trait”Use impl Trait for Type to implement a trait for a type:
type User = { name: string, age: number }
impl Display for User { let display(self) -> string = { `${self.name} (${self.age})` }}The compiler checks that all required methods are implemented. Missing methods produce a clear error.
Default Implementations
Section titled “Default Implementations”Traits can provide default method bodies. Implementors inherit them unless they override:
trait Eq { let eq(self, other: string) -> boolean let neq(self, other: string) -> boolean = { !(self |> eq(other)) }}
impl Eq for User { let eq(self, other: string) -> boolean = { self.name == other } // neq is inherited from the default implementation}Exporting and Importing
Section titled “Exporting and Importing”Traits and their implementations sit on either side of the export/import rule for behaviour:
// Define and export a traitexport trait Display { let display(self) -> string}
// Export every method in a trait impl at onceexport impl Display for User { let display(self) -> string = { self.name }}Import traits with the for prefix — the same syntax used to pull in cross-file for-block methods:
import { User, for Display } from "./types"
impl Display for User { let display(self) -> string = { self.name }}Writing import { Display } for a trait is an error — the compiler asks you to add the for prefix.
Multiple Traits
Section titled “Multiple Traits”A type can implement multiple traits:
impl Display for User { let display(self) -> string = { self.name }}
impl Eq for User { let eq(self, other: string) -> boolean = { self.name == other }}Deriving Traits
Section titled “Deriving Traits”Floe has no built-in deriving syntax. Derives will arrive via the macro system as @derive(Trait) attributes on type declarations, expanding at compile time into generated impl Trait for Type { ... } blocks. Until macros land, write the impl by hand — three lines for Display is a small price for a simpler surface.
What It Compiles To
Section titled “What It Compiles To”Traits are erased at compile time. impl Display for User compiles to exactly the same TypeScript as for User — the trait just tells the checker that a contract is satisfied.
// Floeimpl Display for User { let display(self) -> string = { self.name }}// Compiled TypeScript (identical to plain for-block)function display(self: User): string { return self.name; }No class wrappers, no vtables, no runtime representation. Traits are purely a static checking tool.
- All required methods (those without default bodies) must be implemented
- Default methods are inherited unless overridden
- Traits are compile-time only — no runtime representation
- No orphan rules — scoping via imports handles conflicts
- No trait objects or dynamic dispatch — traits are a static checking tool