Skip to content

Expression Language

Expression Language

MashinTalk’s expression language is a pure, functional sublanguage for computation inside compute steps, conditions, field defaults, and inline expressions. It follows JavaScript conventions with some additions (pipeline operator, pattern matching, comprehensions, optional chaining). The expression language has no I/O primitives by construction; all I/O happens through governed step types.

When to use

Expressions appear in:

  • compute steps: the entire body is an expression
  • decide step conditions: the when clause is an expression
  • filter in read actions: expressions that filter store queries
  • default values: default values on fields can be expressions
  • String interpolation: ${expr} inside string literals

Operators

Operators are listed from highest precedence (binds tightest) to lowest.

PrecedenceOperatorsNotes
Postfix. ?. () []Property access, optional chaining, function calls, indexing
Unary- !/not ~ typeofNegation, logical not, bitwise NOT, type inspection
Exponent**Right-associative
Multiply* / %Multiplication, division, modulo
Add+ -Addition, subtraction, string concatenation
Shift<< >>Bitwise left/right shift
Range..Optional by for step: 1..10 by 2
Pipeline|>Inserts left side as first argument to right side
Comparison< > <= >=Also: is less than, is greater than, is at most, is at least
MembershipinCollection membership test
Equality== !=Also: is, is not
Bitwise AND&
Bitwise XOR^
Bitwise OR|
Logical AND&& / andShort-circuit. Returns left if falsy, right if truthy.
Logical OR|| / orShort-circuit. Returns left if truthy, right if falsy.
Nullish??Returns left if not nil, right if nil
Ternary? :Conditional expression

Natural-language operators

Every symbolic comparison and logical operator has an English alternative that compiles to the same AST. Both forms can be mixed freely.

// These are equivalent:
when score >= 0.9 && category != "spam"
when score is at least 0.9 and category is not "spam"

Truthy/falsy semantics

|| and && use value-returning semantics, not strict boolean. Only nil and false are falsy. 0, "", and [] are all truthy (unlike JavaScript). Use ?? for nil-only coalescing:

value || "default" // returns value if truthy (not nil/false), else "default"
value ?? "default" // returns value if not nil (even if value is false)

Literals

42 // integer
3.14 // decimal
"hello" // string
"Hello ${name}" // template string with interpolation
true, false // boolean
null // null (compiles to nil)
[1, 2, 3] // list
{name: "test", x: 1} // object
{[key]: "value"} // computed property name

String interpolation

Use ${} inside string literals to embed expressions:

"Hello ${input.name}, you have ${steps.count.total} items"
"Status: ${status == "active" ? "Active" : "Inactive"}"
"Score: ${Math.round(score * 100)}%"

Any expression can appear inside ${}. The result is converted to a string.

Context access

ReferenceWhat it accessesExample
input.<field>Machine input fieldsinput.query, input.name
steps.<name>.<field>Output from a previous stepsteps.classify.category
steps.<name>Entire output of a previous stepsteps.classify
state.<field>Machine state (reactive machines)state.counter
context.<field>Execution context valuescontext.user_id

Let bindings

Bind intermediate values to names within a step. Let bindings are scoped to the current step.

compute transform
let count = input.items.length
let total = input.items.reduce((sum, x) => sum + x.price, 0)
let average = total / count
{count: count, total: total, average: average}

Arrow functions

x => x * 2 // single parameter
(a, b) => a + b // multiple parameters
() => 42 // no parameters
(x, y = 0) => x + y // default parameters
x => { let y = x + 1 // block body (last expression is return value)
y * 2 }

Arrow functions are used with array methods (.map, .filter, .reduce) and anywhere a callback is expected.

Destructuring

// In let bindings
let {name, age} = person
let [first, second] = items
let [head, ...tail] = list
// In arrow function parameters
items.map(({name, age}) => name + " (" + age + ")")
pairs.map(([key, value]) => key + "=" + value)

Array methods

Methods dispatched to the Elixir standard library at runtime.

MethodDescriptionExample
.map(fn)Transform each elementitems.map(x => x * 2)
.filter(fn)Keep elements where fn returns trueitems.filter(x => x > 10)
.reduce(fn, init)Accumulate a single valueitems.reduce((sum, x) => sum + x, 0)
.find(fn)First element matching fnitems.find(x => x.id == target)
.some(fn)True if any element matchesitems.some(x => x.active)
.every(fn)True if all elements matchitems.every(x => x.valid)
.includes(val)True if list contains valuetags.includes("urgent")
.indexOf(val)Index of first occurrence (-1 if not found)items.indexOf("target")
.sort()Sort elementsscores.sort()
.reverse()Reverse element orderitems.reverse()
.join(sep)Join elements into a stringwords.join(", ")
.lengthNumber of elements (property, not method)items.length
.flat()Flatten one level of nestingnested.flat()
.flatMap(fn)Map then flattenitems.flatMap(x => x.tags)
.slice(start, end)Extract a sub-listitems.slice(0, 5)

String methods

MethodDescriptionExample
.toUpperCase()Convert to uppercasename.toUpperCase()
.toLowerCase()Convert to lowercaseemail.toLowerCase()
.trim()Remove leading/trailing whitespaceinput.trim()
.split(sep)Split into a listcsv.split(",")
.startsWith(str)True if string starts with strurl.startsWith("https")
.endsWith(str)True if string ends with strfile.endsWith(".pdf")
.includes(str)True if string contains strbody.includes("urgent")
.replace(old, new)Replace first occurrencetext.replace("old", "new")
.replaceAll(old, new)Replace all occurrencestext.replaceAll(" ", "_")
.lengthNumber of characters (property)name.length

Object functions

FunctionDescriptionExample
Object.keys(obj)List of keysObject.keys(metadata)
Object.values(obj)List of valuesObject.values(scores)
Object.entries(obj)List of [key, value] pairsObject.entries(config)

Math functions

FunctionDescription
Math.abs(x)Absolute value
Math.ceil(x)Round up to nearest integer
Math.floor(x)Round down to nearest integer
Math.round(x)Round to nearest integer
Math.trunc(x)Truncate decimal part
Math.sqrt(x)Square root
Math.sign(x)Sign of number (-1, 0, or 1)
Math.sin(x), Math.cos(x), Math.tan(x)Trigonometric functions
Math.asin(x), Math.acos(x), Math.atan(x)Inverse trigonometric
Math.atan2(y, x)Two-argument arctangent
Math.exp(x)e raised to the power x
Math.log2(x), Math.log10(x)Logarithms
Math.random()Random number between 0 and 1
Math.PIPi constant (3.14159…)
Math.EEuler’s number (2.71828…)

JSON functions

FunctionDescriptionExample
JSON.parse(str)Parse a JSON string into a valueJSON.parse(input.payload)
JSON.stringify(obj)Convert a value to a JSON stringJSON.stringify(result)

DateTime functions

FunctionDescriptionExample
DateTime.now()Current datetime (UTC)DateTime.now()
DateTime.today()Current date (UTC)DateTime.today()

Result and Option types

Explicit error and null handling without exceptions.

Result functions

FunctionDescription
Result.ok(v)Wrap a value as a success
Result.error(reason)Wrap a reason as a failure
Result.is_ok(r)True if result is ok
Result.is_error(r)True if result is an error
Result.map(r, fn)Transform the ok value
Result.and_then(r, fn)Chain operations that may fail
Result.unwrap_or(r, default)Extract value or use default
Result.unwrap!(r)Extract value or raise error
Result.try_fn(fn)Wrap a function that might throw

Option functions

FunctionDescription
Option.some(v)Wrap a value as present
Option.none()Represent absence
Option.is_some(o)True if value is present
Option.is_none(o)True if value is absent
Option.map(o, fn)Transform the inner value if present
Option.and_then(o, fn)Chain operations on optional values
Option.unwrap_or(o, default)Extract value or use default
Option.from_nullable(v)Convert a nullable value to an Option
compute safe_parse
let parsed = Result.try_fn(() => JSON.parse(input.text))
let items = Result.map(parsed, data => data.items)
let safe = Result.unwrap_or(items, [])
{items: safe}

Comprehensions

// List comprehensions
[x * 2 for x in items]
[x for x in items if x > 10]
[x + y for x in list1 for y in list2] // nested (cartesian product)
// Map comprehensions
{k: v for {k, v} in entries}
{k: v * 10 for {k, v} in prices if v > 0}

Match expressions

Pattern matching with first-match semantics. The first arm whose pattern matches wins.

match input.action {
"store" => handle_store(input.data)
"query" => handle_query(input.query)
_ => {error: "unknown action"}
}

Pattern types

PatternDescriptionExample
LiteralExact value match"store", 42, true
_Wildcard (matches anything)_ => default_value
VariableBinds the matched valuex => x * 2
Object destructureMatches object structure{type: "billing", amount} => amount
Array destructureMatches array structure[first, ...rest] => first
nullMatches nilnull => "no data"
NestedPatterns inside patterns{items: [first, ...rest]} => first
match input.payload {
{type: "billing", amount} => {team: "billing", value: amount}
{type: "urgent", data} => {team: "escalation", context: data}
[first, ...rest] => {team: first, overflow: rest}
null => {team: "unassigned", error: "no payload"}
_ => {team: "general"}
}

Spread operator

// Object spread (merge/override)
let merged = {...base, key: "override", extra: true}
// Array spread (concatenation)
let extended = [...list, newItem, ...moreItems]

typeof operator

Returns a string naming the runtime type.

typeof "hello" // "string"
typeof 42 // "number"
typeof true // "boolean"
typeof [1, 2] // "list"
typeof {a: 1} // "map"
typeof null // "null"
typeof (x => x) // "function"

Optional chaining

Safe navigation for nested property access. Returns null if any intermediate value is nil.

input.user?.address?.city // null if user or address is nil
steps.lookup?.result?.name // null if lookup failed

Pipeline operator

Inserts the left-hand value as the first argument to the right-hand function.

input.text
|> trim()
|> toLowerCase()
|> split(" ")
|> filter(word => word.length > 3)

Control flow in expressions

// Ternary
condition ? true_value : false_value
// If/else (expression form)
if (condition) { then_branch } else { else_branch }
// Nullish coalescing
value ?? default_when_null

fail() function

Immediately halts the step with an error. Triggers on_failure handlers or flow-level error handling.

compute validate
let valid = input.age > 0 && input.age < 150
if (!valid) fail("Invalid age: must be between 0 and 150")
{validated: true}

Examples

Data transformation pipeline

compute analyze
let items = input.orders.filter(o => o.status == "completed")
let total = items.reduce((sum, o) => sum + o.amount, 0)
let avg = total / items.length
let top = items.filter(o => o.amount > avg).map(o => o.id)
{
completed_count: items.length,
total_revenue: total,
average_order: avg,
above_average_ids: top
}

String processing

compute format_name
let parts = input.full_name.trim().split(" ")
let first = parts[0]
let last = parts.length > 1 ? parts[parts.length - 1] : ""
{
first_name: first,
last_name: last,
display: "${first} ${last.length > 0 ? last[0] + "." : ""}",
initials: first[0].toUpperCase() + (last.length > 0 ? last[0].toUpperCase() : "")
}

Pattern matching with computed values

compute route
let priority = input.amount > 10000 ? "high" : input.amount > 1000 ? "medium" : "low"
match priority {
"high" => {team: "senior", sla_hours: 4}
"medium" => {team: "standard", sla_hours: 24}
"low" => {team: "queue", sla_hours: 72}
}

Governance

The expression language is pure by construction. It has no I/O primitives, no network access, no file system access, and no database access. This purity is not enforced by a sandbox; the capability simply does not exist.

This means:

  • Expressions never require governance approval
  • Expressions never appear in permission checks
  • Expressions always execute, regardless of trust level
  • Expressions produce the same output for the same input (deterministic), except for Math.random() and DateTime.now()

All I/O happens through governed step types (ask, recall, remember, launch, store actions). The expression language computes; machines effect.

See also

  • compute - The primary step type for expressions
  • decide - Branching logic using expression conditions
  • Field Types - Type system for fields
  • defines - Machine-level shared functions used in expressions