Phantom class

Phantom types are marker traits that exist only to provide annotations to the compiler. As a usage of phantom types, let's look at the structure of a list.

Nil = Class()
List T, 0 = Inherit Nil
List T, N: Nat = Class {head = T; rest = List(T, N-1)}

This code results in an error.

3 | List T, 0 = Inherit Nil
TypeConstructionError: since Nil does not have a parameter T, it is not possible to construct List(T, 0) with Nil
hint: use 'Phantom' trait to consume T

This error is a complaint that T cannot be type inferred when List(_, 0).new is used. In such a case, whatever the T type is, it must be consumed on the right-hand side. A type of size zero, such as a tuple of length zero, is convenient because it has no runtime overhead.

Nil T = Class((T; 0))
List T, 0 = Inherit Nil T
List T, N: Nat = Class {head = T; rest = List(T, N-1)}

This code passes compilation. But it's a little tricky to understand the intent, and it can't be used except when the type argument is a type.

In such a case, a phantom type is just what you need. A phantom type is a generalized type of size 0.

Nil T = Class(Impl := Phantom T)
List T, 0 = Inherit Nil T
List T, N: Nat = Class {head = T; rest = List(T, N-1)}

nil = Nil(Int).new()
assert nil.__size__ == 0

Phantom holds the type T. But in fact the size of the Phantom T type is 0 and does not hold an object of type T.

Also, Phantom can consume arbitrary type arguments in addition to its type. In the following example, Phantom holds a type argument called State, which is a subtype object of Str. Again, State is a fake type variable that does not appear in the object's entity.

VM! State: {"stopped", "running"}! = Class(... State)
    start ref! self("stopped" ~> "running") =

The state is updated via the update_phantom! or set_phantom! methods. This is the method provided by the standard patch for Phantom! (the variable version of Phantom), and its usage is the same as the variable update! and set!.