Traits

A trait is a callable type signature. It is declared like a function type:

Show = () -> String

Because traits are types, they are written in PascalCase. The case difference is how the compiler distinguishes a trait implementation (Type.Print) from a regular method (Type.print) on the same type.

Implementing a Trait

A trait is implemented on a type by assigning to Type.TraitName:

Show = () -> String

Greeting = String
Name     = String

Greeting.Show = () -> String {
    "HELLO!"
}

Name.Show = () -> String {
    "Alice"
}

main = (Stdout) -> Noop {
    Greeting("hi").Show().print(Stdout)
    Name("Alice").Show().print(Stdout)
}

Greeting.Show() and Name.Show() both have the same signature (() -> String) and are called the same way.

Multi-Method Traits

A trait with multiple methods is just a product of single-method traits:

Show = Debug & PrintString

Default Implementations

A trait declaration can carry a default body marked { impl }:

Greet = () -> String { impl }

Implementing types may then either override or inherit the default.

Using a Trait as a Parameter

A trait can be used directly as a parameter type. The parameter binds the trait implementation, which is then invocable:

Type.needsPrint = (Print) -> Noop {
    Print()
}

Generic Constraints

Constraints on generic parameters use :, naming a trait the parameter must implement:

List.print = <T: Print>() -> Noop {
    ...
}