TGL: Functions
TGL: Functions
Built-in functions are the only callable functions in TGL. They do not mutate your input or output. Most return null for a wrong type or a missing value, while predicates such as isEmpty and isValidDate return a boolean and typeOf always returns a type name. now() is intentionally time-dependent, so it returns a different value as time advances.
There are five categories: math, string, array, type, and date.
Math
| Function | Returns |
|---|---|
add(a, b, ...) | sum of all arguments (variadic) |
subtract(a, b) | a minus b |
multiply(a, b, ...) | product of all arguments (variadic) |
divide(a, b) | a divided by b (returns null if b is 0) |
mod(a, b) | a modulo b (returns null if b is 0) |
abs(n) | absolute value |
round(n, decimals?) | rounds to N decimals (default 0) |
floor(n) | rounds down |
ceil(n) | rounds up |
min(a, b, ...) | smallest argument (variadic) |
max(a, b, ...) | largest argument (variadic) |
$out.tax = round(multiply($row.subtotal, 0.0825), 2)
$out.bucket = floor(divide($row.age, 10))
$out.peak = max($row.q1, $row.q2, $row.q3, $row.q4) Null handling in math
add and subtract treat null as 0 so accumulator patterns work:
$out.sum = 0
#each $row.values as $v
$out.sum = add($out.sum, $v) // null v contributes 0, doesn't break the chain
/each multiply, divide, mod short-circuit on null. If any argument is null, the result is null. This catches “missing field” cases loudly instead of silently making everything zero.
String
| Function | Returns |
|---|---|
lowerCase(s) | lowercased string |
upperCase(s) | uppercased string |
trim(s) | whitespace stripped from both ends |
trimStart(s) | leading whitespace stripped |
trimEnd(s) | trailing whitespace stripped |
concat(...) | joins arguments into one string (variadic) |
length(s_or_array) | character or element count |
contains(s, search) | true if s contains search |
startsWith(s, prefix) | true if s starts with prefix |
endsWith(s, suffix) | true if s ends with suffix |
substring(s, start, end?) | slice (negative indices count from end) |
split(s, delimiter, limit?) | array of pieces |
replace(s, search, replacement) | replaces first occurrence |
replaceAll(s, search, replacement) | replaces every occurrence |
$out.email = lowerCase(trim($row.email))
$out.full_name = concat($row.first, " ", $row.last)
$out.domain = split($row.email, "@")[1]
$out.is_gmail = endsWith($row.email, "@gmail.com") concat is variadic and converts numbers/booleans to their string form: concat("Hello, ", $row.name, "! You have ", $row.count, " messages") works directly. If any argument is null, the whole result is null. Handle missing values with ?? first.
length is grapheme-aware on strings: length("👋") is 1, not 2. It also works on arrays.
Array
| Function | Returns |
|---|---|
includes(array, value) | true if array contains value |
push(array, value) | new array with value appended |
prepend(array, value) | new array with value prepended |
merge(arr1, arr2, ...) | concatenates arrays |
flat(array, depth?) | flattens nested arrays (default depth 1) |
unique(array) | removes duplicate primitives |
join(array, separator) | joins into a string |
Arrays in TGL are immutable from the script’s perspective. push doesn’t mutate the source, it returns a new array. That means the accumulator pattern looks like:
$out.tags = null
#each $row.raw_tags as $t
$out.tags = push($out.tags, lowerCase($t))
/each push(null, x) returns [x], so the loop builds up an array starting from null without needing a separate initialization.
There are no array literals ([1, 2, 3]). Build arrays via push/prepend or read them off $row.
Type
| Function | Returns |
|---|---|
int(value) | parsed integer (truncates), null if not parseable |
float(value) | parsed float, null if not parseable |
string(value) | string form of a primitive, null for objects/arrays |
bool(value) | true / false / null (only for explicit boolean-like values) |
typeOf(value) | “string” / “number” / “boolean” / “null” / “array” / “object” |
isEmpty(value) | true if value is null, undefined, "", whitespace-only, [], or {} |
The most common use is numeric coercion for CSV-ingested data, where everything arrives as a string:
$out.total = add(int($row["Quantity"]), int($row["Bonus"]))
$out.price = float($row["Price"]) add(string, number) returns null because add is strict about types. int($row["Quantity"]) parses "42" into 42, then add works.
bool is strict: it accepts true/false, 1/0, and "true"/"false" (case-insensitive). It does NOT treat truthiness loosely: bool(2) is null, bool("yes") is null, bool("") is null. Use !!$row.value if you want truthiness; use bool when the source is a real boolean encoded in another type.
isEmpty is the platform’s shared definition of “missing”; useful for branching on optional fields. Unlike ??, which only catches null and undefined, isEmpty also treats empty strings, whitespace-only strings, empty arrays, and empty objects as missing. Real values like 0, false, and "0" are NOT empty:
// normalize missing-shaped values to null in view output
#if isEmpty($row.secondary_type)
$out.secondary_type = null
#else
$out.secondary_type = $row.secondary_type
/if Date
Date functions emit milliseconds since epoch as plain integers. They accept several input forms, including ISO strings, Unix seconds or milliseconds, and timestamp objects with seconds and nanoseconds.
| Function | Returns |
|---|---|
now() | current UTC time in ms |
date(input) | parses any common date format into ms |
isValidDate(input) | true if input parses |
addTime(ms, amount, unit) | shifted timestamp |
startOf(ms, unit) | start of day/hour/etc. |
endOf(ms, unit) | end of day/hour/etc. |
year(ms) / month(ms) / day(ms) | components (UTC) |
weekday(ms) / weekdayName(ms) | 1=Mon..7=Sun, name string |
hour(ms) / minute(ms) / second(ms) / millisecond(ms) | components |
formatDate(ms, format) | formatted string (Luxon tokens) |
toISO(ms) | ISO 8601 string |
toDateString(ms) | YYYY-MM-DD |
toTimeString(ms) | HH:mm:ss |
toMillis(input) / toSeconds(input) | parsed date as ms / whole Unix seconds |
dateDiff(a, b, unit) | difference in unit (days, hours, etc.) |
isWeekend(ms) / isWeekday(ms) | true/false |
isBefore(a, b) / isAfter(a, b) | comparison |
Units accepted by addTime / startOf / endOf / dateDiff: year, month, week, day, hour, minute, second, millisecond (with or without a trailing s).
// Bucket by week
$out.week_start = startOf(date($row.created_at), "week")
// Days since signup
$out.tenure_days = dateDiff(now(), date($row.signup_at), "days")
// Pretty-print
$out.label = formatDate(date($row.created_at), "yyyy-MM-dd HH:mm") If the input doesn’t parse, date() returns null. Date functions that produce a value or formatted string return null for invalid input; date predicates such as isValidDate, isWeekend, and isBefore return false.
Numeric date input below 10 billion is interpreted as Unix seconds; larger numeric input is interpreted as milliseconds. Both toMillis and toSeconds apply that parsing rule before converting to their requested unit.