8.12

4.6 Binding and Annotation🔗ℹ

When a constructor clause creates a constructor that is different enough from the default one, then the binding and annotation forms associated with the class name also should be updated. The binding and annotation clause forms support that customization. Binding and annotation are customized by macros, and similar to constructor customization, they work by relying on an underlying binding or annotation form. But instead of having a super that is mapped to the underlying forms, binding and annotation customization rely on an internal clause to give a name to the more primitive form, and then that name can be used in an excample.

For example, suppose we customize the constructor for a Sandwich class to accept any number of arguments, instead of having the user put those arguments into a list. That is, we want users to write Sandwich("pb", "j") instead of Sandwich(["pb", "j"]). To make pattern-matching binding consistent with that choice, we should also customize the binding, so that Sandwich(top, bottom) would match instead of Sandwich([top, bottom]). Similarly, Sandwich.of(String) would be preferable to Sandwich.of(List.of(String)), which requires adding an of annotation form that is exported from Sandwhich as a namespace.

The customization shown below defines _Sandwich with internal so that it can be used in the binding and annotation expansion:

class Sandwich(ingredients):

  nonfinal

  internal _Sandwich

  constructor (ingredient, ...):

    super([ingredient, ...])

  binding 'Sandwich($ingredient, ...)':

    '_Sandwich([$ingredient, ...])'

  annotation 'Sandwich':

    '_Sandwich'

  annot.macro 'of($ingredient)':

    '_Sandwich.of(List.of($ingredient))'

  export:

    of

> def blt = Sandwich("bacon", "lettuce", "tomato")

> def Sandwich(top, _, _) = blt

> top

"bacon"

> blt is_a Sandwich

#true

> blt is_a Sandwich.of(Number)

#false

When a class has a superclass, the super constructor function is curried for customizing a subclass constructor. The name bound by internal can be used as a constuctor, and it is curried in the same way as superbut don’t use a internal name instead of super, because super adapts to subclass construction, while an internal name always constructs an immediate instance of its class.

An internal name for a subclass is not curried as a binding or annotation form. Instead, it always refers to just the immediate fields of the class. A superclass binding or annotation can be combined with an internal binding or annotation using the && binding operator or && annotation operator.

class Sub(inches):

  extends Sandwich

  internal _Sub

  constructor (~inches: inches, ingredient, ...):

    super(ingredient, ...)(inches)

  binding 'Sub($pre, ..., ~inches: $inches, $post, ...)':

    'Sandwich($pre, ..., $post, ...) && _Sub($inches)'

  annotation 'Sub':

    '_Sub'

  annot.macro 'of($ingredient)':

    '_Sub && Sandwich.of($ingredient)'

  export:

    of

> def blt = Sub("bacon", "lettuce", "tomato", ~inches: 6)

> def Sub(~inches: len, stuff, ...) = blt

> len

6

> [stuff, ...]

["bacon", "lettuce", "tomato"]

> blt is_a Sub.of(String)

#true

> Sandwich("pb", "j") is_a Sub.of(String)

#false