8.12

1.4 Annotations and the Dot Operator🔗ℹ

Besides classes defined with class, a few predefined annotations work with the :~ and :: annotation operators, including Int (meaning exact integer), Number, String, Keyword, and Any (meaning any value).

The :~ and :: operators also work in expression positions. In that case, the assertion or check is about the expression on the left-hand side of :~ or ::. For ::, the left-hand expression must produce a value that satisfies the right-hand annotation, otherwise a run-time exception is thrown. The is_a operator takes an annotation like ::, but it produces a boolean result indicating whether the result of the left-hand expression satisfies the annotation.

> (flip(origin) :~ Posn).x

0

> (1 :: Posn)

::: value does not satisfy annotation

  value: 1

  annotation: Posn

> origin is_a Posn

#true

> 1 is_a Posn

#false

When class defines a new class, an annotation can be associated with each field. When the annotation is written with ::, then the annotation is checked when an instance is created.

class Posn(x :: Int, y :: Int)

> Posn(1, 2)

Posn(1, 2)

> Posn(1, "2")

Posn: value does not satisfy annotation

  value: "2"

  annotation: Int

Naturally, class annotations can be used as field annotations, and then the . operator can be chained for efficient access:

class Line(p1 :~ Posn, p2 :~ Posn)

def l1 :: Line:

  Line(Posn(1, 2), Posn(3, 4))

> l1.p2.x

3

Using . to reach an imported or namespaced binding, as in f2c.fahrenheit_to_celsius, is different than the infix expression operator, ..

More generally, . access is efficient when the left-hand side of . is an expression that can act as a dot provider. A class name also acts as a namespace to provides access to field-accessor functions, as in Posn.x (which doesn’t get a specific x, but produces a function that can be called on a Posn instance to extract its x field). An identifier that is bound using :~ or :: (where a class name follows the :~ or ::) is a dot provider, and it provides access to fields of a class instance. For example, a use of p.x in the lexical context of a p that is bound via p :: Posn is an efficient access to the x field. In general, an annotation that is associated to a binding or expression with :~ or :: might make the binding or expression a dot provider. See Static Information and Binding for more information on dot providers and other static information.

The use_static definition form redefines #%dynamism so that the . operator works only in efficient mode with a dot provider, among others. If the left-hand side of the . is not a dot provider, then . under use_static reports a compile-time error. The use_dynamic form binds #%dynamism to the default #%dynamism, which allows dynamic field lookup if the left-hand side is not a dot provider.

> l1.p2.x

3

> (1).x

x: no such field or method (based on static information)