16. Facets

Overview

Facets are a mechanism to annotate types and slots with metadata. Facets are similar to Java annotations or C# attributes. A facet is a serialized instance of const class declared on a type or slot which implements the Facet mixin.

Facet Classes

Facets are defined as normal classes using the facet positional keyword:

// simple marker facet with no fields
facet class Indexed {}

// struct facet with one or more const fields
facet class Table
{
  const Str name := ""
  const Bool autoCreate
}

All facets classes automatically inherit the Facet mixin. Facets are implied to be const classes which means all their fields must be const.

Facet classes are not allowed to define their own constructor. Instead the compiler will generate one for you. If your facet has fields, then it is implied to be Serializable and all its fields must be serializable types.

The classes above will have the following synthetic definitions:

// marker facet with no instance fields is singleton
const class Indexed : Facet
{
  static const Indexed defVal := Indexed()
  private new make() {}
}

// struct facet with instance fields gets it-block constructor
@Serializable
const class class Table : Facet
{
  new make(|This|? f) { f?.call(this) }
}

Annotations

Any type or slot can be annotated with facets using the @ symbol:

@Indexed
@Table { name = "Employees"; autoCreate = true }
class Employee {}

Struct facets assign their field values inside curly braces similiar to an it-block. Only simple literal expressions may be used in facet annotations which the compiler can determine how to serialize.

Reflection

Facets are available at runtime via the following methods:

Always prefer the facet and hasFacet methods over facets since it is much more efficient. Some examples:

// check if a type is serializable, returns null if not
Serializable? ser := obj.typeof.facet(Serializable#, false)

// check if a field is transient
field.hasFacet(Transient#)

Inheritance

You can annotate a facet class with the FacetMeta facet to declare that a type-level facet should be inherited:

@FacetMeta { inherited = true }
facet class F {}

@F class A {}
class B : A {}

In the example above the class B will inherit the facet F. Facet inheritance works on any type (class or mixin) in the inheritance hierarchy.