logic-and-fallback
title: Logic & Fallback sidebar_position: 4
Logic & Fallback
Part of the function and operator catalog. Every example shown below is
executed as an assertion by the engine's own test suite (examples.test.ts), so a result shown
here is a result the engine actually produces for that input.
IF
IF(condition: any value, thenValue: any value, elseValue: any value) -> any value
Evaluates only the branch its condition selects — the untaken branch is never evaluated at all, so a branch that would divide by zero or read an absent path is safe to write as long as it is never the one taken. Laziness here is not an optimisation: with every arithmetic error hard-failing the run, an eagerly-evaluated IF could not be used as a guard at all. The condition goes through the one condition-position rule: a boolean answers itself, null counts as false, and anything else is a coded error.
Examples:
IF(true, 1, 2)- result:
"1"
- result:
IF(false, 1, 2)- result:
"2"
- result:
IF(input.flag, 1, 2)- context:
{"flag":null} - result:
"2"
- context:
IF(exists(input.divisor), input.total / input.divisor, 0)- context:
{"total":10,"divisor":2} - result:
"5.00000000000000000000"
- context:
IF(exists(input.divisor), input.total / input.divisor, 0)- context:
{"total":10} - result:
"0"
- context:
IF(INDEXOF(input.text, input.needle) == -1, 'not found', 'found')- context:
{"text":"abcabc","needle":"b"} - result:
"found"
- context:
IF(INDEXOF(input.text, input.needle) == -1, 'not found', 'found')- context:
{"text":"abc","needle":"z"} - result:
"not found"
- context:
IF('x', 1, 2)- fails with
EXPR_CONDITION_NOT_BOOLEAN
- fails with
COALESCE
COALESCE(value: any value, ...) -> any value
Returns its first argument that is neither absent nor an explicit null, evaluating arguments left to right and never evaluating any argument after the answer — an argument after the answer that would divide by zero does not fail the expression. Skips an absent path as readily as an explicit null, so a default written for a genuinely optional field works either way. An argument whose own evaluation raises an error still fails the expression (skipping a real failure would be a silent absorb) — only absence and null are skipped. Every argument absent or null is its own coded error, distinct from the collection-lookup no-match code, so a caller can tell a failed fallback chain from a failed lookup.
Examples:
COALESCE(1, 2)- result:
"1"
- result:
COALESCE(input.a, 2)- context:
{"a":null} - result:
"2"
- context:
COALESCE(input.a, input.b, 3)- context:
{"a":null,"b":null} - result:
"3"
- context:
COALESCE(input.a, input.b)- context:
{"b":5} - result:
"5"
- context:
COALESCE(input.a, 1 / 0)- context:
{"a":5} - result:
"5"
- context:
COALESCE(input.a, input.b)- fails with
EXPR_COALESCE_EXHAUSTED
- fails with