Traits

ABS does not support inheritance for code reuse. Method implementations that are common between classes can be defined once and used inside these classes by using traits. A trait is a set of method definitions that can be added to a class via the uses clause in the class definition.

Traits are applied to classes at compile-time and do not change the interface(s) of a class. Classes and their methods are type-checked once all traits are applied.

Traits can re-use and modify other traits via trait operations. A trait can add methods to another trait, modify methods in another trait, or remove methods from another trait.

Syntax

TraitDecl ::=

trait TraitName = TraitExpr

TraitExpr ::=

MethodSet { TraitOper }

MethodSet ::=

TraitName {vbar} MethDecl {vbar} { { MethDecl } }

TraitName ::=

SimpleIdentifier

TraitOper ::=

adds MethodSet

modifies MethodSet

removes ( MethSig {vbar} { { MethSig } } )

A trait is defined at the module level.

The effect of applying a trait T to a class (using uses T inside the class body) is to add the methods in that trait to the class definition.

Trait operations

The trait operation adds adds all the elements of its MethodSet to the trait. It is an error if any method of the MethodSet is already present in the trait, but any error will only be raised after applying all operations, during type checking.

Example:

trait T = { Unit x() { skip; } } ①
trait T2 = { Unit y() { skip; } } adds T ②
T contains the method x.
T2 contains the methods y and x.

The trait operation modifies changes all the methods in the trait to the new implementation described in this MethodSet. If a method with the same name is not present in the trait, the method is added instead.

The methods in the modifies operation may contain original() calls which will call the version of the method before the operation application. In case an overriden method is not present, original() calls will lead to a compile-time error.

Example:

trait T = { Bool m() { return False; } }
trait T2 = { Bool m() { Bool orig = original(); return !orig; } }
class C {
    uses T modifies T2; ①
}
① Calling C.m() will return True.

The operation removes removes the method(s) with the provided signatures from the trait. If a method with the same name is not present in the class (or set of methods), an error will be raised during compilation.

Example:

trait T = { Bool m(){ return False; } }
class C {
    uses T removes { Bool m(); }; ①
}
① Class C does not contain any methods.