Documentation / Traits

Traits

Traits name behaviour a type promises to provide.

The promise stays explicit. Method shapes don't count until the source declares conformance, and every reusable bound stays static before runtime begins.

Choose the explanation level: Trait declarations

Trait declarations

A trait names a behaviour that a type can promise to provide.

A trait with one requirement


    DISPLAY_TEXT must:
        display |This| -> String
    ;
    

This says: any type that claims to be DISPLAY_TEXT must provide a display method that returns a String.

A marker trait


    SERIALIZABLE must:
    ;
    

A marker trait has no methods. It is still a real contract that a type can explicitly adopt.

A trait forms an explicit nominal method contract. A type satisfies a trait only when source declares conformance.

Syntax


    DISPLAY_TEXT must:
        display |This| -> String
    ;
    

Exact contract

  • A trait is an explicit nominal method contract.
  • Trait names use all-caps identifiers.
  • Trait declarations are top-level.
  • TRAIT must: opens a trait declaration.
  • One final semicolon closes the declaration.
  • Requirements are method signatures only.
  • Marker traits with no requirements are valid.
  • Traits are compile-time static contracts.
  • Traits are not runtime values.
  • Static non-method requirements are deferred.
  • Generic traits and generic trait methods are outside scope.

Marker traits


    SERIALIZABLE must:
    ;
    

A marker trait carries no requirements but still represents a deliberate contract that a type may declare.

Traits are not value types

A trait name may appear in trait declarations, conformance declarations and generic bounds. It is invalid as an ordinary variable, parameter, field, return, collection element, choice payload or alias target type.

Related concepts

Choose the explanation level: Trait requirements

Trait requirements

Trait requirements describe the methods a conforming type must have. The receiver has a special name.

This means the implementing type


    DISPLAY_TEXT must:
        display |This| -> String
    ;
    

This is a placeholder for whatever type later declares conformance.

Mutable receiver


    RESETTABLE must:
        reset |~This|
    ;
    

~This means the method may mutate the receiver.

Named This parameters


    SAME must:
        equals |This, other This| -> Bool
    ;
    

When This is not the receiver, it must have a parameter name like other This.

Trait requirements describe the method shapes a conforming type must provide. The receiver is always This or ~This.

Receiver syntax


    RESETTABLE must:
        reset |~This|
    ;

    COMPARABLE_WITH_SELF must:
        same |This, other This| -> Bool
        clone_value |This| -> This
    ;
    

Exact contract

  • Requirement receivers use This or ~This.
  • Lowercase this is source-method syntax and invalid in a requirement.
  • Bare This and ~This are valid only as the first receiver parameter.
  • This is a shared receiver.
  • ~This is a mutable receiver.
  • Direct non-receiver This parameters must be named, such as other This.
  • Direct return This is supported.
  • Composed This forms such as This?, {This} and Box of This are rejected in the Alpha surface.
  • This is trait-local and invalid outside trait declarations.
  • Requirements have no body.
  • Parameter names are not part of the later conformance shape.
  • Parameter access modes, types, return types and return channels are part of the shape.

Multiple requirements

Multiple method requirements may appear in one trait. The current separator between requirements is a newline. Each requirement has no body and no standalone semicolon. The trait itself closes with a final semicolon.


    NAMED must:
        name |This| -> String
        id |This| -> Int
    ;
    

Non-receiver This


    SAME must:
        equals |This, other This| -> Bool
    ;
    

This as a non-receiver parameter must carry a name.

Related concepts

Choose the explanation level: Conformance

Conformance

A type does not satisfy a trait because it happens to have the right methods. It must say so explicitly.

Method plus conformance


    DISPLAY_TEXT must:
        display |This| -> String
    ;

    Label = |
        text String,
    |

    display |this Label| -> String:
        return this.text
    ;

    Label must DISPLAY_TEXT
    

Label must DISPLAY_TEXT is the explicit promise. Without it, the compiler does not treat Label as DISPLAY_TEXT.

Shape must match exactly

The method must have the same receiver mutability, parameter types, return type and return channel. Parameter names do not matter.

Only same-file nominal types

You can conform structs, choices and aligned generic constructors declared in the same file. You cannot conform builtins, imported types or types from another file.

A conformance declaration states that a type implements one or more traits. Matching method shapes alone are not conformance.

Syntax


    Label = |
        text String,
    |

    display |this Label| -> String:
        return this.text
    ;

    Label must DISPLAY_TEXT
    

Exact contract

  • Conformance is explicit.
  • A matching method without Type must TRAIT is not conformance.
  • A conformance declaration is top-level.
  • It is bodyless.
  • It is newline-terminated and has no semicolon.
  • Several traits may be listed with commas.
  • Every requirement must resolve to a method with exact:
    • receiver mutability
    • non-receiver parameter access mode
    • parameter types
    • return types
    • return channels
  • Parameter names do not affect conformance.
  • Accepted user-authored targets are same-file structs, choices and aligned generic constructors.
  • Rejected targets include builtins, imported types, dependency package types, external opaque types and nominal types declared in another file.
  • Canonical evidence is reusable wherever both target type and trait are visible.
  • Conformance does not independently import receiver methods.
  • Concrete values use ordinary visible receiver methods.
  • Specialise-by-concrete-instance conformance is outside scope.

Multiple traits


    Label must DISPLAY_TEXT, SERIALIZABLE
    

Aligned generic constructor conformance

A generic type declared in the same file may declare conformance. The trait requirements use This. The source implementation uses the concrete receiver type and lowercase this.


    Box type A = |
        value A,
    |

    DISPLAY_TEXT must:
        display |This| -> String
    ;

    display type A |this Box of A| -> String:
        return [: box]
    ;

    Box must DISPLAY_TEXT
    

Rejected targets


    -- Error: builtin types cannot declare user-authored conformance
    Int must CASTABLE_TO_STRING
    

Use a local wrapper choice or struct when adapting a value owned by another file or package.

Related concepts

Choose the explanation level: Generic trait bounds

Generic trait bounds

A generic bound says: this placeholder type must provide a specific trait.

One bound


    DISPLAY_TEXT must:
        display |This| -> String
    ;

    render type Item is DISPLAY_TEXT |item Item| -> String:
        return item.display()
    ;
    

Inside render, Item is known to have a display method.

Several bounds


    NAMED must:
        name |This| -> String
    ;

    render_named type Item is DISPLAY_TEXT and NAMED |item Item| -> String:
        return item.display()
    ;
    

and adds another bound to the same parameter.

Generic bounds use traits to promise that a generic parameter provides certain behaviour inside the generic declaration.

Syntax


    DISPLAY_TEXT must:
        display |This| -> String
    ;

    render type Item is DISPLAY_TEXT |item Item| -> String:
        return item.display()
    ;
    

Exact contract

  • Generic bounds use is, not must.
  • and adds bounds to one parameter.
  • Commas separate generic parameters.
  • Bounds apply to generic functions, structs and choices.
  • Each concrete argument requires visible reusable evidence.
  • Trait names are static contracts, not concrete type arguments.
  • A bound may enable a unique required receiver call inside the generic declaration.
  • Bound calls resolve statically to concrete source methods.
  • Concrete values still use ordinary visible methods.
  • Conformance does not import methods into scope.
  • Backends do not receive trait objects or runtime evidence tables.
  • where syntax is rejected.
  • File-local evidence-backed dispatch outside reusable conformance is outside scope.

Multiple bounds on one parameter


    NAMED must:
        name |This| -> String
    ;

    render_named type Item is DISPLAY_TEXT and NAMED |item Item| -> String:
        return item.display()
    ;
    

Multiple parameters with bounds


    render_pair type A is DISPLAY_TEXT, B is DISPLAY_TEXT |left A, right B| -> String:
        return [left.display(), right.display()]
    ;
    

Evidence must be visible

Concrete calls and instantiations require reusable evidence. User-authored reusable evidence originates from the canonical conformance declared with the same-file target type. Callers may reuse that evidence wherever both the type and trait are visible.

Compiler-owned evidence also exists for the builtin cast table.


    Label = |
        text String,
    |

    display |this Label| -> String:
        return this.text
    ;

    Label must DISPLAY_TEXT

    value = render(Label("ok"))
    

Related concepts

Choose the explanation level: Trait incompatibility

Trait incompatibility

Some trait contracts are mutually exclusive. A type cannot claim both.

Two ways to produce text


    TEXT must:
        text |This| -> String
    ;

    FALLIBLE_TEXT must:
        try_text |This| -> String, Error!
    ;

    TEXT must not FALLIBLE_TEXT
    

TEXT promises a String directly. FALLIBLE_TEXT promises a String or an error. A type cannot be both at once.

Symmetric

The relation goes both ways. Saying A must not B also means B must not A in effect.

Trait incompatibility records that two traits cannot both be claimed by the same concrete type.

Syntax


    TEXT must:
        text |This| -> String
    ;

    FALLIBLE_TEXT must:
        try_text |This| -> String, Error!
    ;

    TEXT must not FALLIBLE_TEXT
    

Exact contract

  • Incompatibility is trait metadata.
  • It is symmetric.
  • A concrete type cannot explicitly conform to both sides.
  • Several incompatible traits may be listed.
  • It affects conformance validation only.
  • It does not:
    • add requirements
    • imply negative conformance
    • create trait composition
    • create inheritance
    • affect runtime dispatch

Compiler-owned pairs

The compiler uses must not for incompatible core cast trait pairs. For example, CASTABLE_TO_STRING and TRY_CASTABLE_TO_STRING cannot both be claimed for the same source type. The conforming type is the cast source type. The trait name identifies the cast target.

Related concepts

Choose the explanation level: Core cast traits

Core cast traits

The compiler owns a fixed set of traits that let user types work with cast.

String conversion


    Label = |
        text String,
    |

    to_string |this Label| -> String:
        return this.text
    ;

    Label must CASTABLE_TO_STRING

    value String = cast Label("docs")
    

CASTABLE_TO_STRING means: this type can become a String without failing.

Fallible conversion


    Label = |
        text String,
    |

    try_to_int |this Label| -> Int, Error!:
        return! Error("not a number")
    ;

    Label must TRY_CASTABLE_TO_INT

    number Int = cast Label("42") catch |err|:
        then 0
    ;
    

TRY_CASTABLE_TO_INT means: this type may become an Int, but it can also fail. The method signals failure with return! and the caller recovers with cast ... catch.

The compiler owns a closed set of static cast traits. They provide source evidence for explicit cast and cast! conversions.

Trait table

Each trait requires one receiver method:

  • CASTABLE_TO_INT requires to_int |This| -> Int
  • TRY_CASTABLE_TO_INT requires try_to_int |This| -> Int, Error!
  • CASTABLE_TO_FLOAT requires to_float |This| -> Float
  • TRY_CASTABLE_TO_FLOAT requires try_to_float |This| -> Float, Error!
  • CASTABLE_TO_BOOL requires to_bool |This| -> Bool
  • TRY_CASTABLE_TO_BOOL requires try_to_bool |This| -> Bool, Error!
  • CASTABLE_TO_STRING requires to_string |This| -> String
  • TRY_CASTABLE_TO_STRING requires try_to_string |This| -> String, Error!
  • CASTABLE_TO_CHAR requires to_char |This| -> Char
  • TRY_CASTABLE_TO_CHAR requires try_to_char |This| -> Char, Error!
  • CASTABLE_TO_ERROR requires to_error |This| -> Error
  • TRY_CASTABLE_TO_ERROR requires try_to_error |This| -> Error, Error!

Contract

  • These traits resolve without imports.
  • They cannot be redeclared, imported, exported, aliased or shadowed.
  • They cannot be used as values.
  • The compiler registers builtin evidence for the builtin cast table.
  • Same-file nominal source types and aligned generic constructors may provide evidence.
  • Source method implementations use lowercase this and the concrete receiver type.
  • Explicit conformance is required.
  • Infallible and fallible pairs for one target are incompatible.
  • Evidence resolves before HIR.
  • The traits prove source evidence only.
  • They do not create user-defined target types or a general conversion framework.

Example


    Label = |
        text String,
    |

    to_string |this Label| -> String:
        return this.text
    ;

    Label must CASTABLE_TO_STRING

    value String = cast Label("docs")
    

Infallible versus fallible


    -- Infallible: source always produces the target
    Label must CASTABLE_TO_STRING

    -- Fallible: source may produce the target or an error
    Label must TRY_CASTABLE_TO_INT
    

A fallible conformance requires the matching try_to_* method. The method returns success with return and failure with return!:


    Label = |
        text String,
    |

    try_to_int |this Label| -> Int, Error!:
        value Int = cast! this.text
        return value
    ;

    Label must TRY_CASTABLE_TO_INT

    -- Local recovery with `cast ... catch`.
    number Int = cast Label("42") catch |err|:
        then 0
    ;
    

Propagation through a compatible Error! function uses cast!:


    parse_label |label Label| -> Int, Error!:
        value Int = cast! label
        return value
    ;
    

cast! and catch are mutually exclusive. cast! ... catch is rejected.

Related concepts

Choose the explanation level: Trait design scope

Trait design scope

Traits are for static reuse, not runtime flexibility.

Static reuse


    render type Item is DISPLAY_TEXT |item Item| -> String:
        return item.display()
    ;
    

The compiler knows at compile time which concrete display method to call.

Runtime heterogeneity with choices

When you need different types at runtime, use a choice instead.


    Renderable ::
        LabelItem | value Label |,
        ButtonItem | value Button |,
    ;
    

A choice is a tagged union. The variant tag decides which code to run.

Moth traits stay static. They name behaviour before runtime begins and stay out of the type system as runtime values.

Static model

Trait names are valid in:

  • trait declarations
  • conformance declarations
  • generic bounds

Trait names are invalid as ordinary:

  • variables
  • parameters
  • fields
  • returns
  • collection elements
  • map types
  • choice payloads
  • alias targets

Use a generic bound for static reuse. Use a choice for runtime heterogeneity.


    -- Static reuse
    render type Item is DISPLAY_TEXT |item Item| -> String

    -- Runtime heterogeneity
    Renderable ::
        LabelItem | value Label |,
        ButtonItem | value Button |,
    ;
    

Static traits are backend-independent. Backends do not receive trait objects, erased dispatch or runtime evidence tables.

Deferred

  • static non-method requirements
  • additional compiler-owned builtin conformance families beyond the current closed core-cast evidence table
  • broader standard trait taxonomy that does not create runtime trait values

Outside scope

  • default methods
  • associated types and constants
  • inheritance
  • trait aliases and composition
  • generic traits and generic methods
  • blanket, conditional, negative or specialised conformance
  • structural conformance
  • dynamic trait values and trait objects
  • downcasting and reflection
  • output coercion
  • operator integration
  • automatic primitive conformance
  • derive-style hashing, ordering, formatting, iteration or serialization families

Do not treat compiler-owned test-only or scaffold traits as public language features.

Related concepts