Macros

A macro gives a title to reusable expression syntax. Expansion replaces a call with its template before compilation. The expanded expression follows the expression type and cost rules.

json272 B
{
  "#_ada": {
    "name": "Ada",
    "role": "engineer",
    "age": 36
  },
  "#_bob": {
    "name": "Bob",
    "role": "engineer",
    "age": 41,
    "mentor": {
      "#link": "#_ada"
    }
  },
  "#_cy": {
    "name": "Cy",
    "role": "designer",
    "age": 29
  }
}

Define a macro

A macro document has ordered params and an expands template. An optional description explains its purpose. A stored definition has a title. The native macro builder also accepts an authored definition without saving it.

json199 B
{
  "description": "Multiply an amount by a rate.",
  "params": [
    "amount",
    "rate"
  ],
  "expands": [
    "*",
    {
      "insert": "amount"
    },
    {
      "insert": "rate"
    }
  ]
}

Parameter names are plain text. Each name must differ from the other names. A name cannot be empty or contain whitespace. A macro cannot have a built-in operator's title. Definition validation rejects unknown fields, missing required fields and invalid markers.

Call a macro

A call has the title first, then one argument for each declared parameter.

json49 B
[
  "pricing/apply-rate",
  "?subtotal",
  0.2
]
Result / 4 B
subtotal
100.0
20.0

Expansion produces [* ?subtotal 0.2]. The variable remains a caller input. Expansion rejects an invalid argument count.

json36 B
[
  "pricing/apply-rate",
  100.0
]

Error

Invalid_Call

Insert and spread

An insertion marker is a map with one member. {insert name} replaces the marker with one argument tree. {spread name} inserts each item of a list argument into the surrounding list.

json101 B
{
  "params": [
    "values"
  ],
  "expands": [
    "+",
    {
      "spread": "values"
    }
  ]
}
json52 B
[
  "sum-all",
  [
    1,
    2,
    3,
    4
  ]
]
Result / 2 B
10

The expansion is [+ 1 2 3 4]. A spread marker needs a surrounding list and a list argument. An insertion preserves a list or map as one argument tree.

Compose macros

A template can call another macro. Expansion resolves nested calls before the compiler validates operators and types. Expansion rejects direct and indirect cycles.

json150 B
{
  "params": [
    "value",
    "floor"
  ],
  "expands": [
    ">=",
    {
      "insert": "value"
    },
    {
      "insert": "floor"
    }
  ]
}
json223 B
{
  "params": [
    "value"
  ],
  "expands": [
    "and",
    [
      "at-least",
      {
        "insert": "value"
      },
      18
    ],
    [
      "<",
      {
        "insert": "value"
      },
      65
    ]
  ]
}
json26 B
[
  "working-age",
  36
]
Result / 4 B
true

quote stops macro expansion inside its values. The expression evaluator still rejects quote. Syntax preserved during expansion is not necessarily executable.

Use macros in queries

A query expands macros in expression positions, including predicates, derivations, computed columns, grouping filters, ordering and projection. Ordinary fact values remain data.

json242 B
{
  "find": [
    "?name"
  ],
  "where": [
    [
      "?person",
      "age",
      "?age"
    ],
    [
      "?person",
      "name",
      "?name"
    ],
    [
      "working-age",
      "?age"
    ]
  ],
  "orderBy": [
    "?name"
  ]
}
Result / 82 B
[
  {
    "name": "Ada"
  },
  {
    "name": "Bob"
  },
  {
    "name": "Cy"
  }
]

A mutation uses the same expansion rules in its selection query.

Current and pinned definitions

An unpinned call uses the current title mapping. Each accepted update records a mapping revision. A pin names that mapping revision, rather than the macro entity or its definition revision.

This update changes working-age to require an age of at least 40.

json217 B
{
  "params": [
    "value"
  ],
  "expands": [
    "and",
    [
      ">=",
      {
        "insert": "value"
      },
      40
    ],
    [
      "<",
      {
        "insert": "value"
      },
      65
    ]
  ]
}
json26 B
[
  "working-age",
  36
]
Result / 5 B
false

The first recorded mapping revision of working-age is 13 in this fresh fixture. Read the actual revision from the saved definition in your database. A pinned call keeps that earlier rule.

json72 B
[
  {
    "title": "working-age",
    "mappingRevision": 13
  },
  36
]
Result / 4 B
true

A query can pin all calls of a title with its definitions member. Expansion consumes that metadata before query compilation.

json288 B
{
  "definitions": {
    "working-age": 13
  },
  "find": [
    "?name"
  ],
  "where": [
    [
      "?person",
      "age",
      "?age"
    ],
    [
      "?person",
      "name",
      "?name"
    ],
    [
      "working-age",
      "?age"
    ]
  ],
  "orderBy": [
    "?name"
  ]
}
Result / 82 B
[
  {
    "name": "Ada"
  },
  {
    "name": "Bob"
  },
  {
    "name": "Cy"
  }
]

Expansion rejects an unknown mapping revision. It does not select the current rule.

Run it

build macro · expand macro.