Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Built-in functions

ORBIT provides a fixed set of pure functions for mapping data between steps: null handling, conversions, string and collection helpers, numeric helpers, and collection functions that take a lambda. Like all expressions, they are deterministic and perform no I/O. This page lists every function with its signature, argument and result types, behavior with null, runtime errors, and examples.

Summary

FunctionSignatureResult
coalescecoalesce(a, b, ...)the first non-null argument
is_nullis_null(x)Bool
lenlen(String | List<T> | Map<String, T>)Int
stringstring(x)String
intint(Int | Float | String | Bool)Int
floatfloat(Int | Float | String | Bool)Float
containscontains(haystack, needle)Bool
lowerlower(String)String
upperupper(String)String
trimtrim(String)String
joinjoin(List<T>, String)String
keyskeys(Map<String, T>)List<String>
minmin(number, ...)number
maxmax(number, ...)number
absabs(number)number
sumsum(List<Int> | List<Float>)Int or Float
mapmap(List<T>, T => U)List<U>
filterfilter(List<T>, T => Bool)List<T>
findfind(List<T>, T => Bool)T?
anyany(List<T>, T => Bool)Bool
allall(List<T>, T => Bool)Bool

General rules:

  • Function names are case-sensitive, and there are no user-defined functions.
  • An unknown name or a wrong number of arguments is E0407. The diagnostic suggests a close name or prints the signature.
  • An argument of the wrong type is E0400 (argument 1 of `len` must be a String, List or Map, found `Int` ).
  • Arguments of type JSON are accepted everywhere and checked at runtime.
  • A runtime error, such as int("abc"), fails the step being scheduled (or the workflow, in return) with EXPRESSION_ERROR. See Expressions.

coalesce

coalesce(a: T?, b: T?, ..., z: T) -> T

Returns the first argument that is not null, or null if all are null. Takes one or more arguments.

AspectBehavior
Argument typesMust be compatible with each other (E0400 otherwise). Int and Float combine to Float.
Result typeThe combined type, non-optional if any argument is non-optional, optional otherwise.
EvaluationAll arguments are evaluated.
workflow Coalesce {
    version "1.0.0"
    input { nickname: String?, name: String?, discount: Float? }
    return {
        display: coalesce(input.nickname, input.name, "customer")   // String
        maybe: coalesce(input.nickname, input.name)                 // String?
        discount: coalesce(input.discount, 0)                       // Float
    }
}

coalesce is the standard way to turn an optional value, such as a field of a skipped step, into a required one. See Conditions.

is_null

is_null(x: T) -> Bool

Returns true if x is null. Accepts any type. is_null(x) is equivalent to x == null.

workflow IsNull {
    version "1.0.0"
    input { coupon: String? }
    return { has_coupon: !is_null(input.coupon) }
}

len

len(x: String | List<T> | Map<String, T>) -> Int
ArgumentResult
Stringthe number of Unicode characters (code points), not bytes: len("héllo") is 5
List<T>the number of items
Map<String, T>the number of keys
null0

Optional arguments are accepted, and null counts as empty. Other types are E0400.

workflow Len {
    version "1.0.0"
    input { items: List<LineItem>, note: String? }
    return { item_count: len(input.items), note_length: len(input.note) }
}

string

string(x: T) -> String

Converts any value to a string:

ArgumentResult
a Stringunchanged
null"null"
a numberits JSON form: string(42) is "42", string(1.5) is "1.5"
a Bool"true" or "false"
a list, map or recordcompact JSON: string([1, 2]) is "[1,2]", string({ a: 1 }) is "{\"a\":1}"
workflow ToString {
    version "1.0.0"
    input { order_id: String, attempt: Int }
    return { key: input.order_id + "#" + string(input.attempt) }
}

int

int(x: Int | Float | String | Bool) -> Int
ArgumentResult
Intunchanged
Floattruncated toward zero: int(2.9) is 2, int(-2.9) is -2
Stringparsed as a base-10 integer after trimming whitespace: int(" 42 ") is 42; int("1.5") and int("abc") are runtime errors
Bool1 for true, 0 for false
nullruntime error

The compiler accepts optional arguments, but a null value fails at runtime. Use int(coalesce(x, 0)) when the argument may be null.

float

float(x: Int | Float | String | Bool) -> Float
ArgumentResult
Int or Floatthe value as a float: float(5) is 5.0
Stringparsed after trimming whitespace: float("2.5") is 2.5; an invalid string is a runtime error
Bool1.0 for true, 0.0 for false
nullruntime error

Use float to force float arithmetic on a value that may arrive as a JSON integer: float(input.price) / 2. See Arithmetic.

Before ORBIT 0.2, the compiler accepted a Bool argument but the runtime rejected it with EXPRESSION_ERROR.

contains

contains(haystack: String, needle: String) -> Bool
contains(haystack: List<T>, needle: T) -> Bool
contains(haystack: Map<String, T>, key: String) -> Bool
HaystackResult
Stringtrue if needle is a substring; contains("hello", "ell") is true
List<T>true if an item equals needle, using structural equality (contains([1, 2], 2.0) is true)
Map<String, T> or a recordtrue if the key exists
nullfalse

The compiler checks only the number of arguments (2), not their types. A combination outside the table, such as a string haystack with a number needle, is a runtime error.

workflow Contains {
    version "1.0.0"
    input { tags: List<String>, email: String }
    return {
        vip: contains(input.tags, "vip")
        corporate: contains(lower(input.email), "@example.com")
    }
}

lower, upper, trim

lower(s: String) -> String
upper(s: String) -> String
trim(s: String) -> String
FunctionResult
lowerUnicode lowercase: lower("ÀB") is "àb"
upperUnicode uppercase
trimthe string without leading and trailing Unicode whitespace

With a String? argument the result is String?, and null returns null. Other argument types are E0400.

workflow Normalize {
    version "1.0.0"
    input { email: String, nickname: String? }
    return {
        email: lower(trim(input.email))          // String
        nickname: upper(input.nickname)          // String?
    }
}

join

join(items: List<T>, separator: String) -> String

Concatenates the items, with separator between them. String items are used as they are; other items are converted to their JSON form, as with string: join([1, 2], ",") is "1,2". An empty list gives "".

The compiler accepts an optional list, but a null list is a runtime error.

workflow Join {
    version "1.0.0"
    input { skus: List<String> }
    return { summary: join(input.skus, ", ") }
}

keys

keys(m: Map<String, T>) -> List<String>

Returns the keys of a map (or the field names of a record), in the order they appear in the JSON object. A null argument is a runtime error.

workflow Keys {
    version "1.0.0"
    input { labels: Map<String, String> }
    return { label_names: keys(input.labels) }
}

min, max

min(a: number, b: number, ...) -> number
max(a: number, b: number, ...) -> number

Return the smallest or largest argument. Take one or more arguments, each Int or Float (or JSON). Optional arguments are E0400; wrap them in coalesce.

AspectBehavior
Result typeInt if every argument is Int, otherwise Float.
Result valueThe winning argument itself, unchanged: max(3, 2.5) returns 3.
TiesThe first of the equal arguments wins.
workflow Clamp {
    version "1.0.0"
    input { requested: Int }
    return { batch_size: max(1, min(input.requested, 500)) }
}

abs

abs(x: Int) -> Int
abs(x: Float) -> Float

Returns the absolute value. The result type is the argument type. An optional argument is E0400. abs of the minimum Int overflows and is a runtime error.

workflow Abs {
    version "1.0.0"
    input { expected: Float, actual: Float }
    return { drift: abs(input.actual - input.expected) }
}

sum

sum(list: List<Int>) -> Int
sum(list: List<Float>) -> Float

Adds the elements of a list of numbers. The result is Int for a List<Int> and Float otherwise. The list may be optional; sum of null and of an empty list is 0. An element that is not a number is a runtime error (possible only with JSON values), and so is Int overflow. Any other argument type is E0400 (argument 1 of `sum` must be a List of Int or Float, found `List<String>` ).

At runtime, the result is an integer whenever every element is an integer JSON number, so the sum of an empty List<Float> is the JSON number 0, not 0.0. JSON consumers that distinguish the two should not rely on the representation.

workflow Invoice {
    version "1.0.0"
    input { quantities: List<Int>, prices: List<Float> }
    return { units: sum(input.quantities), total: sum(input.prices) }
}

Collection functions

map, filter, find, any and all take a list and a lambda: a one-parameter function written parameter => expression. The function calls the lambda once per element, in list order, with the parameter bound to the element. See Expressions for the lambda syntax.

Rules shared by all five:

  • They take exactly two arguments (E0407 otherwise, with the signature in a note). The first must be a list (E0400: argument 1 of `map` must be a List, found `String` ); a List<T>? is accepted, and null behaves like an empty list. The second must be a lambda (E0400: argument 2 of `any` must be a lambda).
  • The lambda body can read input, execution, step outputs, loop variables and the parameters of enclosing lambdas. Reading a step’s output adds a dependency, as anywhere else.
  • For filter, find, any and all, the body must have type Bool (E0400, or E0405 for a Bool?). At runtime a null condition counts as false.
  • The parameter must not reuse the name of a step, a loop variable, an enclosing lambda parameter, input or execution (E0413).
  • A lambda anywhere else is E0415.
  • An error inside the body, such as a division by zero for one element, fails the whole call.

map

map(list: List<T>, f: T => U) -> List<U>

Returns the list of f(x) for each element x.

workflow Emails {
    version "1.0.0"
    input { users: List<{ email: String, name: String }> }
    return { emails: map(input.users, u => lower(u.email)) }
}

filter

filter(list: List<T>, f: T => Bool) -> List<T>

Returns the elements for which f is true, in their original order.

workflow Restock {
    version "1.0.0"
    input { items: List<{ sku: String, stock: Int }> }
    return { low: map(filter(input.items, i => i.stock < 5), i => i.sku) }
}

find

find(list: List<T>, f: T => Bool) -> T?

Returns the first element for which f is true, or null when there is none. The result is always optional.

workflow Primary {
    version "1.0.0"
    input { addresses: List<{ city: String, primary: Bool }> }
    return { city: coalesce(find(input.addresses, a => a.primary).city, "unknown") }
}

any, all

any(list: List<T>, f: T => Bool) -> Bool
all(list: List<T>, f: T => Bool) -> Bool

any is true when f is true for at least one element, and all when it is true for every element. Both stop at the first element that decides the result. For an empty list, any is false and all is true.

task "mail.send" {
    input { to: List<String> }
    output { sent: Int }
}

workflow Notify {
    version "1.0.0"
    input { users: List<{ email: String, active: Bool }> }

    step send {
        worker "mail.send"
        when any(input.users, u => u.active)
        with { to: map(filter(input.users, u => u.active), u => u.email) }
    }
}

A workflow that uses a lambda anywhere compiles to orbit-ir/2. See Versioning.

See also