---
tags: help, language
order: 40
description: the expression language by example, and every core function
---
# Help: expressions

Expressions compute values: in rules, in computed fields (`@calc`), in object queries (`?query=`), in ```` ```table ```` fences, and in the [Fiddle](/fiddle), where you can try every line below. Each example shows the expression, then its value.

## Values

- `42` → `42`
- `2.5` → `2.5`
- `"hello"` → `"hello"`
- `'single quotes too'` → `"single quotes too"`
- `true` → `true`
- `[1, 2, 3]` → `[1, 2, 3]`
- `{a: 1, b: "x"}` → `{a: 1, b: "x"}`
- `null ?? "none"` → `"none"`

Numbers are `Int` (whole, 64-bit, wraps like Java's `long`) or `Float`. `Number` accepts either. Text is `String`. Dates and durations have their own types, below.

## Arithmetic

- `1 + 2 * 3` → `7`
- `(1 + 2) * 3` → `9`
- `99 / 2` → `49.5`
- `4 / 2` → `2.0`
- `7 % 3` → `1`
- `1 + 2.5` → `3.5`

`/` always gives a `Float`. Dividing by zero is an error (`E_ARITH`), never infinity.

## Text

- `"n=${1 + 2}"` → `"n=3"`
- `"a" + "b"` → `"ab"`
- `"ell" in "hello"` → `true`
- `toUpper("abc")` → `"ABC"`
- `split(s = "a,b,c", by = ",")` → `["a", "b", "c"]`

Write `\${` for a literal `${`. Triple quotes (`"""…"""`) hold text over several lines.

## Comparing and logic

- `1 is 1` → `true`
- `1 is not 2` → `true`
- `5 is "5"` → `false`
- `1 < 2 and 3 > 2` → `true`
- `false or true` → `true`
- `not false` → `true`
- `2 in [1, 2]` → `true`
- `3 not in [1, 2]` → `true`
- `"a" in {a: 1}` → `true`

`is` and `==` are the same, as are `and`/`&&`, `or`/`||`, `not`/`!`. `and` and `or` take Booleans only: for a default value use `??`. Comparisons don't chain: `1 < 2 < 3` is an error.

## Missing values

- `x ?? 7` → `7`
- `x is defined` → `false`
- `x is empty` → `true`
- `[] is empty` → `true`
- `{a: null}.a ?? "none"` → `"none"`

Reading a name that has no value is an error (`E_UNDEFINED`) unless you test it or give it a default. `?.` reads a field of something that may be null.

## Choosing

- `if 2 > 1 then "yes" else "no"` → `"yes"`
- `(if true then 1 else 2) + 5` → `6`

An `if` in an expression always has an `else`.

## Types and casts

- `5 is a Int` → `true`
- `5.5 is a Int` → `false`
- `"x" is a String` → `true`
- `"42" as Int` → `42`
- `4.9 as Int` → `4`
- `12 as String` → `"12"`
- `typeOf(1 + 1)` → `"Int"`

## Objects and lists

- `{a: 1} + {b: 2}` → `{a: 1, b: 2}`
- `{a: {x: 1}} ++ {a: {y: 2}}` → `{a: {x: 1, y: 2}}`
- `{a: 1, b: 2} - {b: ""}` → `{a: 1}`
- `[1, 2] + 3` → `[1, 2, 3]`
- `[1] ++ [2]` → `[1, 2]`
- `[1, 2, 3][-1]` → `3`
- `{"a-b": 2}["a-b"]` → `2`
- `{a: 5}.a` → `5`

`+` merges objects one level deep (the right side wins); `++` merges all the way down and joins lists. A negative index counts from the end.

## Working with lists

- `[1, 2, 3] map (x => x * 2)` → `[2, 4, 6]`
- `[1, 2, 3] filter (x => x > 1)` → `[2, 3]`
- `[1, 2, 3] exists (x => x > 2)` → `true`
- `[[1], [2, 3]] flatMap (x => x)` → `[1, 2, 3]`
- `[1, 2, 3] fold (acc = 0) (x => acc + x)` → `6`
- `[1, 2, 3] mkString ", "` → `"1, 2, 3"`
- `[{id: "a"}, {id: "b"}] indexBy "id"` → `{a: {id: "a"}, b: {id: "b"}}`
- `{a: 1} map (p => p.key + p.value)` → `["a1"]`
- `ids flatMap par (i => http.json(url = "https://…/" + i))`: `flatMap`, with the web calls running at once, at most 4 at a time (`flatMap par 8 (…)` for up to 8, at most 32); results stay in the order of `ids`. `map par` is the same for `map`. If one fails, the others finish first, then the error is raised. With nothing to wait for, `par` changes nothing.
- `[1, 2, 3] filter (x => x > 1) map (x => x * 10)` → `[20, 30]`

The part in parentheses is a small function: `x => …` takes each item as `x`. On an object, `map`, `filter` and `exists` see `{key, value}` pairs.

## Patterns

- `"/acct/42" matches /\/acct\/(?<id>\d+)/` → `true`
- `"ab" ~= /a./` → `true`

A pattern is written between slashes, not quotes. In a rule, named groups like `(?<id>…)` become variables after a successful match.

## Dates and durations

- `("2026-09-24" as Date) < ("2026-09-25" as Date)` → `true`
- `("2026-09-24T00:00:00Z" as Date) + "2h30m"` → `"2026-09-24T02:30:00.000Z"`
- `("2026-09-25" as Date) - ("2026-09-24" as Date)` → `"1d"`
- `"90s" as Duration` → `"1m30s"`
- `isoDate("2026-09-24T10:00:00Z" as Date)` → `"2026-09-24"`

A duration is text like `"5s"`, `"5 seconds"`, `"2h30m"`, `"1d"`; next to a date it's read as a duration. Dates are ISO text when stored.

## Core functions

Functions take named arguments, `split(s = "a,b", by = ",")`; one that takes a single argument also takes it bare, `sizeOf([1, 2])`; a variable with the argument's name can stand for it, `split(s, by)`. `f(obj...)` passes an object's fields as the arguments.

- `sizeOf(x)`: length of a String or list, number of fields of an object.
- `typeOf(x)`: the type's name.
- `now()`: the current date and time. `today()`: today at 00:00 UTC.
- `isoDate(d)`: a Date as `YYYY-MM-DD`.
- `toMillis(x)`: a Date or Duration as milliseconds.
- `uuid()`: a random id.
- `trim(s)`, `toUpper(s)`, `toLower(s)`.
- `split(s, by)`: text to a list.
- `replaceAll(s, regex, with)`, `replaceFirst(s, regex, with)`: the regex given as text.
- `slice(x, from, until)`: part of a String or list; `until` optional, negatives count from the end.
- `range(from, until)`: the Ints from `from` up to `until - 1`.
- `flatten(xs)`: one level of nested lists.
- `math.sum(xs)`, `math.min(xs)`, `math.max(xs)`, `math.average(xs)`: over a list of numbers; average is always a Float.
- `nicej(x)`: pretty JSON text.
- `hashcode(x)`: a sha1 of the value.
- `urlencode(s)`, `base64encode(s)`, `base64decode(s)`.
- `json.parse(text)`: JSON text to a value.
- `csv.parse(text)`: CSV with a header row to a list of rows; numeric cells become numbers.
- `http.json(url, headers)`, `http.text(url, headers)`: fetch a web page (https, public sites, 10 s, 2 MB), in rules and flows only: the flow pauses while it waits, and the server carries on. In a computed field, a table or a query it's an error (`E_WAIT`): fetch in a rule and store the result.

In rules there are also the engine's own messages: `log(msg)`, `diesel.throw(code, msg)`, `dom.upsert(cls, entity)`, `dom.find(cls, key)`, `dom.list(cls)`, `dom.remove(cls, key)`.

## When it goes wrong

Errors are never silent: they say what went wrong and where. The common ones are `E_UNDEFINED` (a name with no value: use `??` or `is defined`), `E_TYPE` (the wrong kind of value, like `"a" or true`), `E_PARSE` (the text isn't an expression), `E_ARITH` (division by zero), `E_ARG` (a function's arguments) and `E_NO_TOOL` (no such function).
