TGL: Operators

TGL: Operators

TGL has comparison, logical, and nullish-coalescing operators. It deliberately doesn’t have arithmetic operators. This page also covers TGL’s strict-typing semantics, which interact with operators in ways worth knowing about.

Comparison

$out.is_admin = $row.role == "admin"
$out.is_recent = $row.age < 30
$out.has_orders = $row.order_count >= 1
$out.eligible = $row.status != "banned"

Available: ==, !=, <, >, <=, >=. == does strict equality (no type coercion, so "5" == 5 is false).

Logical

$out.flagged = $row.suspicious && $row.score < 0.5
$out.notify = $row.subscribed || $row.opted_in
$out.is_invalid = !$row.is_valid

&& and || short-circuit. ! negates.

Truthiness is standard: null, undefined, 0, "", false, and NaN are falsy; everything else is truthy.

Nullish coalescing: ??

$out.label = $row.label ?? "untitled"
$out.country = $row.address.country ?? $row.user.country ?? "US"

a ?? b returns a unless a is null or undefined, in which case it returns b. Critically, ?? does NOT treat 0, "", or false as missing. Only null/undefined triggers the fallback. Use this when you want a default for “really missing,” not for “falsy.”

$out.count = $row.count ?? 0      // 0 if count is null
$out.checked = $row.opted_in ?? false  // false if opted_in is null

What’s not here: arithmetic

There are no +, -, *, /, % operators. Use the math functions instead:

$out.total = multiply($row.price, $row.qty)
$out.with_tax = add($out.total, multiply($out.total, $row.tax_rate))
$out.average = divide($out.total, $row.count)

The reason: arithmetic operators in JS-like languages have surprising coercions ("5" + 3 is "53", null + 1 is 1, [] + {} is "[object Object]"). TGL’s function-call form makes the type intent explicit. add accepts numbers and treats null as 0; other types return null. There is no silent string-numeric mixing.

Type semantics

Strict typing

Functions don’t coerce silently. If add gets a string, you get null:

$out.total = add($row.x, $row.y)
// row.x = 5, row.y = 3       returns 8
// row.x = "5", row.y = 3     returns null  (string isn't a number)
// row.x = null, row.y = 3    returns 3     (null treated as 0 in add)
// row.x = 5, row.y = "abc"   returns null  (still null, abc isn't a number)

This is intentional. Silent type coercion can create subtle data bugs. TGL surfaces a math type mismatch as null. If your math involves a numeric string, add an int(...) or float(...) conversion.

Null propagation

Most functions return null when given null (or any wrong type). This propagates: lowerCase(trim($row.email)) returns null cleanly when email is missing, instead of throwing.

The exceptions are documented per-function. Most notably add and subtract, which treat null as 0 to make accumulators ergonomic.

What’s next