Macros
MACRO() turns a feature expression into a reusable function. You specify which inputs become parameters, and the result is a callable that you can invoke with different values — like defining a function in any programming language.
MACRO(expression USING param1, param2, ...) Creating and calling a macro
Define a feature expression, then wrap it in MACRO() to make it callable. Here, AREA_CIRCLE_FEATURE computes the area of a circle, and AREA_CIRCLE_FUNCTION turns it into a function that accepts a radius:
DEFINE
radius := INPUT(DOUBLE),
area_circle_feature := PI() * POW(radius, 2e0),
area_circle_function := MACRO(area_circle_feature USING radius)
RETURN
area_circle_value_for_2 := "AREA_CIRCLE_FUNCTION"(2e0),
area_circle_value_for_5 := "AREA_CIRCLE_FUNCTION"(5e0)
;| AREA_CIRCLE_VALUE_FOR_2 DOUBLE | AREA_CIRCLE_VALUE_FOR_5 DOUBLE |
|---|---|
| 12.566370614359172 | 78.53981633974483 |
The macro is called like a regular function — AREA_CIRCLE_FUNCTION(2e0) evaluates the area formula with radius 2.
Inline expressions
You don't need to name the intermediate feature. The expression can go directly inside MACRO(), and arguments can be expressions too:
DEFINE
radius := INPUT(DOUBLE),
area_circle_function := MACRO(PI() * POW(radius, 2e0) USING radius)
RETURN
area_circle_value_for_2 := "AREA_CIRCLE_FUNCTION"(1e0 + 1e0),
area_circle_value_for_5 := "AREA_CIRCLE_FUNCTION"(2.5e0 * 2e0)
;| AREA_CIRCLE_VALUE_FOR_2 DOUBLE | AREA_CIRCLE_VALUE_FOR_5 DOUBLE |
|---|---|
| 12.566370614359172 | 78.53981633974483 |
Multiple parameters
Macros accept any number of input parameters. The order of parameters in the USING clause determines the argument order when calling:
DEFINE
length := INPUT(DOUBLE),
width := INPUT(DOUBLE),
area_rectangle_function := MACRO(length * width USING length, width)
RETURN
area_rectangle_value_for_2_5 := "AREA_RECTANGLE_FUNCTION"(2e0, 5e0),
area_rectangle_value_for_5_3 := "AREA_RECTANGLE_FUNCTION"(5e0, 3e0)
;| AREA_RECTANGLE_VALUE_FOR_2_5 DOUBLE | AREA_RECTANGLE_VALUE_FOR_5_3 DOUBLE |
|---|---|
| 10.0 | 15.0 |
Chaining syntax
Macros support FeatureQL's chaining syntax. The chained value becomes the first argument:
DEFINE
radius := INPUT(DOUBLE),
area_circle_function := MACRO(PI() * POW(radius, 2e0) USING radius)
RETURN
area_circle_value_for_2_chaining := 2e0."AREA_CIRCLE_FUNCTION"()
;| AREA_CIRCLE_VALUE_FOR_2_CHAINING DOUBLE |
|---|
| 12.566370614359172 |
2e0.AREA_CIRCLE_FUNCTION() is equivalent to AREA_CIRCLE_FUNCTION(2e0).
How arguments are substituted
A call is a variant of the macro body that replaces each parameter with its argument. Three rules follow:
- Arguments reach the whole body graph. Every body feature that depends on a parameter is rewritten, not only the top expression.
- Arguments are computed in the caller's graph. An argument can be a feature that the body also uses, or another parameter, without looping.
- Calls are independent. Two calls of the same macro in one query do not affect each other or the original features.
Here TOTAL depends on PRICE both directly and through TAX. The call TOTAL_FN(TAX, 0.5e0) uses the caller's TAX (20) as the price, so the body computes 20 + 20 × 0.5 = 30:
DEFINE
price := INPUT(DOUBLE),
rate := INPUT(DOUBLE),
tax := price * rate,
total := price + tax,
total_fn := MACRO(total USING price, rate)
RETURN
total,
total_of_tax := "TOTAL_FN"(tax, 0.5e0),
total_of_200 := "TOTAL_FN"(200e0, rate)
FOR CROSS
price := BIND_VALUE(100e0),
rate := BIND_VALUE(0.2e0)
;| TOTAL DOUBLE | TOTAL_OF_TAX DOUBLE | TOTAL_OF_200 DOUBLE |
|---|---|---|
| 120.0 | 30.0 | 240.0 |
Limitations
Macros are not polymorphic — each input parameter accepts only the type declared in its INPUT() definition. If you need a function that works across multiple types (e.g., both DATE and TIMESTAMP), use a UDF instead.
When to use macros vs variants
MACRO() | VARIANT() | |
|---|---|---|
| Purpose | Create reusable function templates | Create alternative versions of existing features |
| Mechanism | Parameterizes a feature expression | Replaces dependencies in an existing DAG |
| Best for | DRY patterns, reusable calculations | Experimentation, A/B testing, what-if analysis |
See Variants for dependency replacement and UDFs for polymorphic custom functions.