Traits in a Nushell

Reusable groups of methods: Traits are units of behavior. They are sets of methods that serve as the behavioral building block of classes and primitive units of Code Reuse [2].

In addition to offering behavior, traits also require methods, i.e., methods that are needed so that trait behavior is fulfilled. Traits do not define state, instead they require accessor methods.

Fig. A1 shows a class SyncStream that uses two traits, TSyncReadWrite and TStream. The trait TSyncReadWrite provides the methods syncRead, syncWrite and hash. It requires the methods read and write, and the two accessor methods lock and lock:. We use an extension to UML to represent traits (the right column lists required methods while the left one lists the provided methods).

Explicit composition: A class contains a super-class reference, uses a set of traits, defines state (variables) and behavior (methods) that glue the traits together; a class implements the required trait methods and resolves any method conflicts. Trait composition respects the following three rules:

Methods defined in the composer take precedence over trait methods. This allows the methods defined in a composer to override methods with the same name provided by the used traits; we call these methods glue methods.

Flattening property. In any class composer the traits can be in principle in-lined to give an equivalent class definition that does not use traits.

Composition order is irrelevant. All the traits have the same precedence, and hence conflicting trait methods must be explicitly disambiguated.

Conflict resolution: While composing traits, method conflicts may arise. A conflict arises if we combine two or more traits that provide identically named methods that do not originate from the same trait. There are two strategies to resolve a conflict: by implementing a (glue) method at the level of the class that overrides the conflicting methods, or by excluding a method from all but one trait. Traits allow method aliasing; this makes it possible to introduce an additional name for a method provided by a trait. The new name is used to obtain access to a method that would otherwise be unreachable because it has been overridden [2].

[…]