Online edition for BLOGE
0.9.8-RC1· facts verified 2026-09-15 · 中文
Appendix A — Built-in Functions
Every guard, constraint, and expression in bloge DSL has the same toolbox of built-in functions available. This appendix is the single reference for that toolbox: every name, every signature, every gotcha. Bookmark this page once you start writing real DSL.
When You Need This Reference
| Question | Section |
|---|---|
What does coalesce / ifNull / isNull do? | Null & Default Handling |
Can I do regex inside a when: guard? | String & Regex |
| How do I sum or average a list field? | Math & Aggregation |
| How do I read a value out of a nested map? | Map & Object |
| How do I render a timestamp or compute a duration? | Date & Time |
| How do I sign or hash a payload? | Optional Crypto Modules |
| How do I check whether a function call is allowed at compile time? | Purity & Compile-Time Evaluation |
Find a Function by Task
| I need to… | Start with |
|---|---|
| clean, split, match, or format text | String functions |
| total, compare, round, or aggregate numbers | Math and aggregation |
| filter, map, flatten, or inspect a list | Collection functions |
| read nested object or map fields safely | Map/object plus null handling |
| convert a value before schema validation | Type conversion |
| calculate a deadline or duration | Date/time functions |
| create a digest or signature | Optional crypto module |
Chapter 5 (Branches that Decide) introduces these functions inside when:
guards. Chapter 7 (Designing Good Operators) uses them inside operator
constraints and usageExample. Chapter 15 (State Machines) uses them inside
transition guards. Wherever you see an expression in bloge DSL, this list is
what you can call.
Function Call Syntax
All functions use standard call syntax:
when: concat("Hello, ", ctx.name) == "Hello, Alice"
when: contains(split(ctx.tags, ","), "premium")
when: padLeft(toString(ctx.id), 10, "0")
bloge does not use a Unix-style pipe operator (|) for function
composition. The | token is reserved by the lexer for the logical OR
operator (||). Compose functions by nesting calls.
Null Handling Convention
Core built-ins are null-safe by default. Passing null either:
- returns
null(safe-nav semantics), or - returns a sensible default —
size(null) → 0,isEmpty(null) → true,concat(null, "x") → "x", etc.
You can rely on this convention to write fewer defensive guards. The optional crypto module is the one exception — see its section below.
Purity & Compile-Time Evaluation
Most functions are pure — given the same input they always produce the same output and have no side effects. The DSL compiler is allowed to fold pure-function calls at compile time when arguments are constant.
A small number of functions are impure and may not be folded:
now()— current timestamp from the engine's time sourcetoday()— current date from the engine's time sourceuuid()— random UUIDsecret(name)— secret lookup (crypto module)
In strict compile-time-only contexts (constants, schema constraints) the compiler will reject impure calls. Use them inside runtime guards instead.
1. String Functions (17)
| Function | Signature | Returns | Description |
|---|---|---|---|
concat | concat(arg1, arg2, …) | String | Concatenates all arguments as strings. null arguments are treated as empty. |
substring | substring(str, start [, end]) | String | Substring from start (inclusive) to end (exclusive); clamps to bounds. |
uppercase / upper | uppercase(str) | String | Upper-case the string. |
lowercase / lower | lowercase(str) | String | Lower-case the string. |
trim | trim(str) | String | Strip leading and trailing whitespace. |
replace | replace(str, oldStr, newStr) | String | Replace every literal occurrence of oldStr. |
startsWith | startsWith(str, prefix) | Boolean | Prefix test; false on null. |
endsWith | endsWith(str, suffix) | Boolean | Suffix test; false on null. |
indexOf | indexOf(str, target) | Number | Index of first occurrence, or -1. |
length / len | length(str) | Number | String length. Also works on lists and maps. 0 on null. |
matches | matches(str, regex) | Boolean | Regex match. Returns false on null or invalid pattern (no exception). |
replaceAll | replaceAll(str, regex, replacement) | String | Regex replace-all. Returns original on null or bad pattern. |
split | split(str, delimiter) | List | Split by literal delimiter (not regex). Empty list on null. |
join | join(list, delimiter) | String | Join list elements with delimiter. Null elements become empty strings. |
padLeft | padLeft(str, length, padChar) | String | Left-pad to length using a single pad character. |
padRight | padRight(str, length, padChar) | String | Right-pad to length using a single pad character. |
when: startsWith(ctx.email, "admin@") and contains(ctx.email, "@")
when: matches(ctx.phone, "^\\+?[0-9 ]{6,}$")
when: padLeft(toString(ctx.orderId), 8, "0") == "00012345"
2. Math & Aggregation (10)
| Function | Signature | Returns | Description |
|---|---|---|---|
abs | abs(n) | Number | Absolute value. |
min | min(a, b) | Number | Minimum of two numbers. |
max | max(a, b) | Number | Maximum of two numbers. |
round | round(n) | Number | Round to nearest integer (Math.round). |
ceil | ceil(n) | Number | Round up to nearest integer. |
floor | floor(n) | Number | Round down to nearest integer. |
clamp | clamp(n, min, max) | Number | Constrain n to [min, max]. |
pow | pow(base, exponent) | Number | base ^ exponent. |
sum | sum(list) | Number | Sum of numeric elements. Null elements skipped. Empty list → 0. |
avg | avg(list) | Number | Average of numeric elements. Empty/null → null. |
when: sum(ctx.items.*.price) > 1000
when: clamp(ctx.retries, 0, 5) == ctx.retries
3. Collection Functions (13)
| Function | Signature | Returns | Description |
|---|---|---|---|
size | size(collection) | Number | Size of list, map, or string. 0 on null. |
contains | contains(collection, item) | Boolean | Membership. Lists → exact match; strings → substring; maps → key lookup. |
first | first(list) | any | First element, or null. |
last | last(list) | any | Last element, or null. |
isEmpty | isEmpty(collection) | Boolean | True if empty or null. |
distinct | distinct(list) | List | Remove duplicates, preserve order. |
flatten | flatten(listOfLists) | List | Flatten one level. Non-list elements pass through. |
sort | sort(list) | List | Sort using natural order (falls back to toString); nulls first. |
take | take(list, n) | List | First n elements. |
drop | drop(list, n) | List | Skip first n elements. |
reverse | reverse(list) | List | Reverse list order. |
any | any(list, value) | Boolean | True if any element equals value. |
all | all(list, value) | Boolean | True if every element equals value. Empty list → true. |
when: contains(ctx.tags, "premium") and size(ctx.items) > 0
when: any(ctx.errors, "TIMEOUT")
when: all(distinct(ctx.statuses), "OK")
4. Map & Object (5)
| Function | Signature | Returns | Description |
|---|---|---|---|
keys | keys(map) | List | All keys as a list. |
values | values(map) | List | All values as a list. |
entries | entries(map) | List | List of {key, value} maps. |
merge | merge(map1, map2) | Map | Shallow merge; map2 overwrites map1. |
has | has(map, key) | Boolean | Whether the map contains the key. |
when: has(ctx.headers, "X-Trace-Id")
when: size(keys(ctx.attributes)) > 3
5. Null & Default Handling (3)
| Function | Signature | Returns | Description |
|---|---|---|---|
coalesce | coalesce(v1, v2, …) | any | First non-null argument, or null if all are null. |
isNull | isNull(value) | Boolean | Value is null. |
isNotNull | isNotNull(value) | Boolean | Value is not null. |
coalesce is the workhorse for default values. ifNull (see Advanced)
is a two-argument variant that reads more naturally for a single fallback.
when: isNotNull(ctx.userId)
let region = coalesce(ctx.region, ctx.country, "GLOBAL")
6. Type Conversion (5)
| Function | Signature | Returns | Description |
|---|---|---|---|
toString | toString(value) | String | Convert any value to its string form. |
toNumber | toNumber(value) | Number | Parse number from string, or return the numeric value. Invalid strings coerce to 0. |
toBoolean | toBoolean(value) | Boolean | Parse "true" / "false" or pass through boolean. |
toInt | toInt(value) | Number | Convert to integer; truncates decimals. |
typeOf | typeOf(value) | String | "String", "Number", "Boolean", "List", "Map", "Object", or "Null". |
when: toNumber(ctx.amountStr) > 100
when: typeOf(ctx.payload) == "Map"
7. Date/Time (6)
All dates flow as ISO-8601 strings (yyyy-MM-ddTHH:mm:ssZ). Use these
helpers to format, parse, add, and diff them.
| Function | Signature | Returns | Pure? | Description |
|---|---|---|---|---|
now | now() | String | ❌ | Current timestamp from GraphEngine.currentTimeSource(). |
today | today() | String | ❌ | Current date yyyy-MM-dd (UTC). |
formatDate | formatDate(isoStr, pattern) | String | ✅ | Format ISO instant with a DateTimeFormatter pattern (e.g. "yyyy-MM-dd HH:mm"). |
parseDate | parseDate(str, pattern) | String | ✅ | Parse with a pattern; returns an ISO-8601 instant. |
addDuration | addDuration(isoStr, durationStr) | String | ✅ | Add a duration. Accepts "2h", "30m", "1d", "500ms", or ISO-8601 form "PT2H". |
diffDuration | diffDuration(iso1, iso2, unit) | Number | ✅ | Distance between two instants. unit: "s"/"seconds", "m"/"minutes", "h"/"hours", "d"/"days". |
when: diffDuration(ctx.createdAt, now(), "h") > 24
let expiresAt = addDuration(now(), "30m")
Determinism tip. Inside replay-sensitive contexts (durable sessions, deterministic tests) prefer
parseDate/addDurationdriven by stored timestamps overnow()/today().
8. Utility / ID (2)
| Function | Signature | Returns | Pure? | Description |
|---|---|---|---|---|
uuid | uuid() | String | ❌ | Random UUID v4. |
format | format(template, arg1, arg2, …) | String | ✅ | Java String.format style placeholders (%s, %d, …). Returns template on format error. |
let traceId = uuid()
let message = format("Order %s failed after %d retries", ctx.orderId, ctx.retries)
9. Advanced Utility (8)
| Function | Signature | Returns | Description |
|---|---|---|---|
regexExtract | regexExtract(str, pattern [, group]) | String | First match for a regex group (default 1); null if no match. |
regexExtractAll | regexExtractAll(str, pattern) | List | All non-overlapping matches. |
getField | getField(obj, fieldName) | any | Read a field from a map or bean (reflection); null-safe. |
setField | setField(obj, fieldName, value) | Map | Return a shallow copy with the field updated (immutable semantics). |
template | template(str, bindings) | String | Replace ${key} placeholders using a map of bindings. |
range | range(start, end) | List | Integer sequence [start, end). |
mapOf | mapOf(k1, v1, k2, v2, …) | Map | Build a map from alternating key/value arguments. |
listOf | listOf(e1, e2, …) | List | Build a list from arguments. |
ifNull | ifNull(value, default) | any | value if non-null, otherwise default. |
let host = regexExtract(ctx.url, "https?://([^/]+)/")
let updated = setField(ctx.user, "lastSeenAt", now())
let greeting = template("Hello, ${name}!", mapOf("name", ctx.name))
10. JSON Functions (2)
JSON helpers are registered when a JsonCodec is provided to the DSL
compiler. They are part of BuiltInFunctions.registerAll(registry, jsonCodec).
| Function | Signature | Returns | Description |
|---|---|---|---|
toJson | toJson(obj) | String | Serialize to JSON via the configured codec. |
fromJson | fromJson(str) | any | Deserialize JSON to Map / List / primitive. |
let payload = toJson(mapOf("orderId", ctx.orderId, "total", ctx.total))
let parsed = fromJson(ctx.responseBody)
If no JsonCodec is configured, calls to these names will fail at DSL
compilation with an unknown-function error.
Optional Modules (bloge-functions-crypto)
The bloge-functions-crypto module is loaded automatically via Java
ServiceLoader when its JAR is on the classpath. It exposes three families of
functions. Crypto functions that resolve secrets require a SecretProvider to
be configured on the engine.
Crypto (5)
| Function | Signature | Returns | Pure? | Description |
|---|---|---|---|---|
hmacSha256 | hmacSha256(data, keyRef) | String | ✅ | HMAC-SHA256 hex digest. keyRef is resolved via SecretProvider. |
hmacSha512 | hmacSha512(data, keyRef) | String | ✅ | HMAC-SHA512 hex digest. |
aesEncrypt | aesEncrypt(plaintext, keyRef [, ivRef]) | String | ✅ | AES-256-CBC; base64 output. Random IV when not supplied. |
aesDecrypt | aesDecrypt(ciphertext, keyRef [, ivRef]) | String | ✅ | AES-256-CBC; expects base64 input. |
secret | secret(name) | String | ❌ | Resolve a secret by name from the SecretProvider. |
Hash (4)
| Function | Signature | Returns | Description |
|---|---|---|---|
md5 | md5(str) | String | MD5 hex digest. |
sha1 | sha1(str) | String | SHA-1 hex digest. |
sha256 | sha256(str) | String | SHA-256 hex digest. |
sha512 | sha512(str) | String | SHA-512 hex digest. |
Encoding (6)
| Function | Signature | Returns | Description |
|---|---|---|---|
base64Encode | base64Encode(str) | String | Base64 encode UTF-8 string. |
base64Decode | base64Decode(str) | String | Base64 decode; null on invalid input. |
hexEncode | hexEncode(str) | String | Hex encode UTF-8 string. |
hexDecode | hexDecode(str) | String | Hex decode; null on invalid input. |
urlEncode | urlEncode(str) | String | URL encode (UTF-8). |
urlDecode | urlDecode(str) | String | URL decode (UTF-8); null on invalid input. |
let signature = hmacSha256(ctx.requestBody, "webhook-secret")
let fingerprint = sha256(ctx.userId)
let safeQuery = urlEncode(ctx.search)
Crypto module exception. Unlike core functions, crypto/hash functions may throw on invalid input (bad keys, malformed ciphertext). Validate inputs at the boundary or wrap calls inside operators that handle the failure mode you want.
Common Mistakes
- Pipe operator.
value | uppercaseis not valid bloge DSL. Writeuppercase(value). The lexer reads|as the start of||. - Regex split.
split(s, ",")splits on the literal string",", not a regex. UsereplaceAllfirst if you need pattern-based splitting. - Date math without ISO strings.
addDuration("2024-01-01", "1d")will fail — the first argument must be an ISO-8601 instant. UseaddDuration(parseDate("2024-01-01", "yyyy-MM-dd"), "1d")if you have a bare date. - Mutating maps.
setFielddoes not mutate in place; it returns a copy. Bind the result:let updated = setField(ctx.user, "name", "Alice"). - Impure calls in constants.
now()anduuid()are rejected in compile-time-only positions (constant folding, schema constants). Move them into runtime guards or operator outputs. - Crypto without
SecretProvider.hmacSha256/aesEncryptwill fail ifSecretProvideris not configured on the engine. Wire it in your Spring or manual bootstrap (see Chapter 20). toBooleanon numbers.toBoolean(1)returnsnull, nottrue. Only"true"/"false"strings and actual booleans are accepted.
Relationship to Main Chapters
- Chapter 5 — Branches that Decide. Where
when:guards are introduced — the primary place you compose these functions. - Chapter 7 — Designing Good Operators. Uses
constraintsandusageExampleexpressions; the same function set applies. - Chapter 14 — Multi-Turn Sessions and Chapter 15 — State Machines.
Use guard expressions for session phase transitions and state-machine
transitions; both compile through the same
ExpressionEvaluator. - Chapter 9 — Tooling Workflow. The
operator-metadata.jsonexporter surfaces these function names to IDE tooling so completion and validation match what the compiler will accept. - Chapter 20 — Spring and Production Wiring. Where
JsonCodecandSecretProviderare configured, enabling the optional JSON and crypto helpers above.
Verification. The 72-function core surface matches
BuiltInFunctionsTest.registerAll_populatesExpectedNumberOfEntries() in
bloge-core. Add the two JSON helpers and the eleven crypto/hash/encoding
helpers to reach the 85 functions documented here.