Companion Modules

In Flix every enum and trait declaration is associated with a companion module.

Enum Companions

When we declare an enum, its type and cases are automatically available inside its companion module. For example, we can write:

enum Color {
    case Red,
    case Green,
    case Blue
}

mod Color {
    pub def isWarm(c: Color): Bool = match c {
        case Red    => true
        case Green  => false
        case Blue   => false
    }
}

Here the Color type and the Red, Green, and Blue cases are automatically in scope within the companion Color module.

Trait Companions

Every trait declaration also gives rise to a companion module.

For example, we can define a trait Addable for types whose elements can be added:

trait Addable[t] {
    pub def add(x: t, y: t): t
}

The Addable trait implicitly introduces a companion module Addable. We typically use the companion module to store functionality that is related to the trait.

For example, we could have:

mod Addable {
    pub def add3(x: t, y: t, z: t): t with Addable[t] = add(add(x, y), z)
}

When accessing a member of Addable, Flix will automatically look in both the trait declaration and its companion module. Consequently, Addable.add refers to the trait member add whereas Addable.add3 refers to the function inside the Addable module. Note that the add signature is in the scope of the Addable module.

We should be aware that functions defined in the companion module of a trait cannot be redefined by instances of the associated trait. Thus we should only put members into the companion namespace when we do not intend to redefine them later.