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.
{
"#_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.
{
"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.
[
"pricing/apply-rate",
"?subtotal",
0.2
]
- subtotal
- 100.0
20.0
Expansion produces [* ?subtotal 0.2]. The variable remains a caller input.
Expansion rejects an invalid argument count.
[
"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.
{
"params": [
"values"
],
"expands": [
"+",
{
"spread": "values"
}
]
}
[
"sum-all",
[
1,
2,
3,
4
]
]
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.
{
"params": [
"value",
"floor"
],
"expands": [
">=",
{
"insert": "value"
},
{
"insert": "floor"
}
]
}
{
"params": [
"value"
],
"expands": [
"and",
[
"at-least",
{
"insert": "value"
},
18
],
[
"<",
{
"insert": "value"
},
65
]
]
}
[
"working-age",
36
]
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.
{
"find": [
"?name"
],
"where": [
[
"?person",
"age",
"?age"
],
[
"?person",
"name",
"?name"
],
[
"working-age",
"?age"
]
],
"orderBy": [
"?name"
]
}
[
{
"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.
{
"params": [
"value"
],
"expands": [
"and",
[
">=",
{
"insert": "value"
},
40
],
[
"<",
{
"insert": "value"
},
65
]
]
}
[
"working-age",
36
]
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.
[
{
"title": "working-age",
"mappingRevision": 13
},
36
]
true
A query can pin all calls of a title with its definitions member. Expansion
consumes that metadata before query compilation.
{
"definitions": {
"working-age": 13
},
"find": [
"?name"
],
"where": [
[
"?person",
"age",
"?age"
],
[
"?person",
"name",
"?name"
],
[
"working-age",
"?age"
]
],
"orderBy": [
"?name"
]
}
[
{
"name": "Ada"
},
{
"name": "Bob"
},
{
"name": "Cy"
}
]Expansion rejects an unknown mapping revision. It does not select the current rule.