8.15.0.4

4.7 Private Fields and Methods🔗ℹ

Sometimes, a class needs extra fields in its objects that are not visible outside the class’s implementation. The class form supports two ways of declaring private fields. One way is to use private field in the class body:

class Car(mpg):

  private field gas = 10

  method go(dist):

    gas := gas - dist/mpg

> def c = Car(30)

> c.go(240)

> c

Car(30)

Another way is to use the private modifier for a field listed in parentheses after the class name. Also including a default-value expression with = avoids the need for a custom constructor, since it is automatically omitted from the default constructor.

class Car(mpg, private mutable gas = 10):

  method go(dist):

    gas := gas - dist/mpg

> def c = Car(30)

> c.go(240)

> c

Car(30)

Both approaches work in this example, because the field is mutable. If a private field is immutable, then it needs to be written with other fields in parentheses, because a field declaration in a class body always creates a mutable field. If the initial value of a private depends on values supplied when an object is created, then a custom constructor may be needed. When a private field is declared within parentheses after a class name, then the underlying constructor accessed with superas used in a custom constructor—accepts values for private fields as well as public ones.

class Car(make, model, private mpg):

  private field gas: 0

  constructor(make, model):

    let [tank_size, mpg] = lookup_specs(make, model)

    let car = super(make, model, mpg)

    car.gas := tank_size

    car

  method go(dist):

    gas := gas - dist/mpg

> def c = Car("Mazda", "Miata")

> c.go(240)

> c

Car("Mazda", "Miata")

Private fields are visible only within methods of the same class when accessed directly or through the class’s annotation, within a constructor through the class’s annotation, or through an internal name’s static annotation. For example, declaring an internal name _Car makes a private gas field accessible outside the class’s implementation:

class Car(mpg):

  internal _Car

  private field gas = 10

  method go(dist):

    gas := gas - dist/mpg

> def c = Car(30)

> c.go(240)

> c.gas

gas: no such field or method

  in value: Car(30)

> (c :~ _Car).gas

2

Methods can be private too, following essentially the same rules as private fields. Private methods can be useful as helpers within a class’s implementation, or via an internal name that is selectively exported, they can be used to limit access to some methods.

Fields and methods can be protected, which is like private, except that protected fields and methods are accessible in subclasses, and they can be overridden when not final.