Skip to content

Custom macros#

CEL runtimes can be extended with custom macros. Unlike functions, macros transform the expression at compile time.

Custom macros are runtime-specific. Expressions using custom macros only work with runtimes that implement them.

Macros vs functions#

Functions are evaluated at runtime with concrete values. Macros rewrite the abstract syntax tree (AST) during compilation.

// The built-in has() macro transforms at compile time
has(msg.field)

// Becomes something like (conceptually)
msg.field != default_value

Macros can:

  • Introduce new identifiers (like x in list.all(x, x > 0))
  • Short-circuit evaluation
  • Access unevaluated expressions

When to use macros#

Use macros when you need to:

  • Introduce iteration variables
  • Transform expressions before type checking

Use functions for most other cases, as they are simpler to implement.

Runtime specifics#

The mechanism for defining custom macros varies by runtime. Macro implementation requires working with the CEL AST.

  • cel-go: Use parser.NewGlobalMacro() or parser.NewReceiverMacro()
  • cel-java: Use CelMacro
  • cel-cpp: Use Macro

See also#

  • Custom functions - Runtime function extensions
  • Has - Built-in presence macro
  • All - Built-in quantifier macro