8.16.0.4

6.4 Binding and Annotation Macros🔗ℹ

Macros can extend binding-position syntax, too, via bind.macro. In the simplest case, a binding operator is implemented by expanding to other binding operators, like this definition of $$$ as a prefix operator to constrain a pattern to number inputs:

import:

  rhombus/meta open

bind.macro '$$$ $n':

  '$n :: Number'

> def $$$salary = 100.0

> salary

100.0

More expressive binding operators can use a lower-level protocol where a binding is represented by transformers that generate checking and binding code. It gets complicated, and it’s tied up with the propagation of static information, so the details are in Binding Low-Level Protocol. After an expressive set of binding forms are implemented with the low-level interface, however, many others can be implemented though simple expansion.

The annot.macro form is similar to bind.macro, but for annotations.

use_static

 

annot.macro 'PosnList': 'List.of(Posn)'

 

fun nth_x(ps :~ PosnList, n):

  ps[n].x

Annotations and binding patterns serve similar and interacting purposes. The :~ and :: binding operators put annotations to work in a binding. For the other direction, the matching annotation operator puts a binding form to work in a annotation.

For example, suppose you want a annotation PersonList, which is a list of maps, and each map must at least relate "name" to a String and "location" to a Posn. The Map.of annotation combination cannot express a per-key specialization, but the Map binding pattern can.

annot.macro 'PersonList':

  'List.of(matching({"name": (_ :: String),

                     "location": (_ :: Posn)}))'

def players :: PersonList:

  [{"name": "alice", "location": Posn(1, 2)},

   {"name": "bob", "location": Posn(3, 4)}]

As another example, here’s how a ListOf annotation constructor could be implemented if List.of did not exist already:

annot.macro 'ListOf ($ann ...)':

  'matching([_ :: ($ann ...), $('...')])'

At a lower level, the bridge between binding patterns and annotations is based on their shared use of static information as described in the binding API and the annotation API.