Skip to main content

Expressions

Expressions are used throughout defacto definitions: in guards, computed properties, field mappings, per-effect conditions, and identity normalization. They evaluate against event data and entity state to produce values.

Syntax

Field access

Expressions can reference event data and entity properties using dot notation.

event.email # field from the normalized event
entity.mrr # property on the current entity
event.user.address.city # nested field (dot-path into JSON)
event.items.0.product_id # array indexing via dot-path
value # bare variable (used in identity normalize expressions)

Missing fields return null rather than raising an error.

Literals

42 # integer
3.14 # float
"hello" # string (double quotes)
'hello' # string (single quotes)
true / false # boolean
null # null
30d / 24h / 90m # duration (days, hours, minutes, seconds)

Operators

From lowest to highest precedence:

OperatorDescription
or, ||Logical or (short-circuit)
and, &&Logical and (short-circuit)
==, !=Equality
>, <, >=, <=Comparison
+, -Addition, subtraction (also string concat, datetime arithmetic)
*, /Multiplication, division
**Exponentiation
not, !Logical not (unary)

Conditionals

if event.amount > 100 then "high" else "low"

Indexing

Bracket indexing works on any expression that returns an array.

split(event.email, "@")[1] # domain part of an email
event.items[-1] # last element (negative indexing)

Out-of-bounds access returns null.

Type coercion

Arithmetic operations between Int and Float coerce to Float. String + String produces concatenation. DateTime + Duration produces a new DateTime. DateTime - DateTime produces a Duration.

Functions

String (12)

FunctionSignatureReturnsDescription
str::to_lowercase(String)StringConvert to lowercase
str::to_uppercase(String)StringConvert to uppercase
str::trim(String)StringRemove leading and trailing whitespace
str::substring(String, Int, Int)StringExtract substring (start, length). Character-based, not byte-based
starts_with(String, String)BoolCheck if string starts with prefix
ends_with(String, String)BoolCheck if string ends with suffix
contains(String, String)BoolCheck if string contains substring
contains(Array, value)BoolCheck if array contains value
replace(String, String, String)StringReplace all occurrences of second arg with third
regex_match(String, String)BoolTest if string matches regex pattern
regex_extract(String, String)String or nullReturn first capture group, or null if no match
to_string(any)StringConvert any value to its string representation
len(String)IntString length
len(Array)IntArray length

Math (6)

FunctionSignatureReturnsDescription
math::abs(Int or Float)same typeAbsolute value
floor(Float)IntRound down
ceil(Float)IntRound up
round(Float)IntRound to nearest integer
min(number, number)numberSmaller of two values. Mixed Int/Float coerces to Float
max(number, number)numberLarger of two values. Mixed Int/Float coerces to Float

Date/Time (13)

FunctionSignatureReturnsDescription
now()DateTimeCurrent UTC time
days_between(DateTime, DateTime)IntAbsolute difference in days
hours_between(DateTime, DateTime)IntAbsolute difference in hours
minutes_between(DateTime, DateTime)IntAbsolute difference in minutes
days_since(DateTime)IntDays elapsed since the given time
year_of(DateTime)IntYear component
month_of(DateTime)IntMonth component (1-12)
day_of(DateTime)IntDay of month component (1-31)
hour_of(DateTime)IntHour component (0-23)
minute_of(DateTime)IntMinute component (0-59)
day_of_week(DateTime)IntDay of week (0=Monday through 6=Sunday)
date_trunc(DateTime, String)DateTimeTruncate to unit: "year", "month", "day", "hour", "minute"
parse_datetime(String, String)DateTimeParse string with strftime format. Assumes UTC

Array (2)

FunctionSignatureReturnsDescription
split(String, String)ArraySplit string by delimiter
join(Array, String)StringJoin array elements with delimiter. Non-strings are coerced

Type casting (2)

FunctionSignatureReturnsDescription
to_int(value)IntConvert from Float (truncates), Bool (true=1), or String
to_float(value)FloatConvert from Int or String

Null handling (1)

FunctionSignatureReturnsDescription
coalesce(value, value, ...)first non-nullReturns the first non-null argument. Variadic (at least 1 arg)

Examples

Guards:

guard: "event.plan != entity.plan"
guard: "event.amount > 0 and days_since(entity.last_purchase) < 365"
guard: "contains(event.email, '@company.com')"
guard: "len(event.items) > 0"

Computed properties:

compute: "entity.mrr * 12"
compute: "if days_since(entity.last_order) > 90 then 'inactive' else 'active'"
compute: "date_trunc(entity.signup_date, 'month')"

Field mappings:

mappings:
domain: { compute: "split(event.email, '@')[1]" }
full_name: { compute: "str::trim(event.first_name + ' ' + event.last_name)" }

Identity normalization:

identity:
email:
normalize: "str::to_lowercase(str::trim(value))"

Duration arithmetic:

guard: "event.timestamp > entity.signup_date + 30d"