8.12

3.5 Annotations versus Binding Patterns🔗ℹ

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. Converter annotations, as opposed to predicate annotations, also connect bindings and annotations as described in Annotations as Converters.