trait

A trait is a named collection of related attibutes ( fields ). Traits are not intended to hold data, but simply model a group of fields. Container UDTs such as entity, key, record, and union can use traits by using the includes keyword.

Multiple assert blocks can be specified to perform validation. Asserts defined as part of a trait are part of asserts in definitions that include the trait.

trait Shape {
    Colour : string
}

record Rectangle includes Shape {
    Width : int
    Height : int
}

record Circle includes Shape {
    Radius : int
}

By includes of Shape, Rectangle and Circle will contain the Colour attribute.

Traits can be used extensively to model UDTs with common attributes, or as simply a marker.

trait Datasource {
}

record File includes Datasource {
    Name : string
}

record Database includes Datasource {
    ConnString : string
}

A trait can include one or many traits.

trait HealthFacts {
    CalorieCount : int
}

trait FoodItem {
    ExpiryDate : date
}

trait FrozenFood {
    StorageTemperature : int
}

record FrozenPizza includes HealthFacts, FoodItem, FrozenFood {
    Toppings : list< string >
}

When combining traits in this way fields get aggregated, so FrozenPizza will contain 4 fields.

Controlling scope of trait usage

When creating a trait it maybe necessary to constrain what other types can use it via includes.

To express such a constraint, a scope can be added to the trait. This is similar to ‘sealed’ types in programming languages.

trait Datasource scope File, Database { }

record File includes Datasource { ... }

record Database includes Datasource { ... }

In the example above, File and Database are specified in the scope of Datasource, therefore are the only type permitted to use Datasource as part of includes.

Attempts to use Datasource as as includes, such as in the example below, will be reported as an error as UrlDownload is not specified in the scope of Datasource.

record UrlDownload includes Datasource { ... }

Diamond-shaped Dependencies

Consider the following change on the declaration above.

trait FrozenFood includes HealthFacts {
    StorageTemperature : int
}

By doing so, FrozenPizza has 2 possible paths to the CalorieCount field - via FrozenFood and direct.

ALFA emits a warning message when diamond-shaped dependency cases are encountered, however it will continue with a single CalorieCount field and it is not considered an error. In order to stop the warning, the field can be explicitly declared in the base of the diamond types - FrozenPizza in the example.

Multiple assert blocks can be specified to perform validation.