Statement
A Statement is an action that can be taken in response to a message. Statements form the body of an on clause which is what handlers are composed of. Statements express the business logic of your system in a structured but abstract way.
Statement Types¶
| Statement | Description | Example |
|---|---|---|
when |
Conditional logic with optional else | when total > Minimum then { ... } end |
match |
Pattern matching over a typed subject | match status { case Pending { ... } } |
foreach |
Bounded iteration over a collection | foreach line in field order.lines { ... } |
send |
Emit a message on one of this processor's outlets | send event X to outlet Events |
tell |
Deliver a message directly to a processor | tell command X to entity Y |
yield |
Produce a command's or query's declared response | yield result Info(id) |
set |
Assign a value to a field or state | set field status to "Active" |
let |
Create a local variable binding | let total = call function Cart.Total(items) |
put |
Publish a value to a UI output | put order.number to output Panel |
return |
Return a function's result | return call function Tax.Compute(sub) |
require |
Assert a precondition | require amount > Zero |
do |
Natural language action description | do "Calculate the total" |
error |
Refuse to proceed, with a reason | error "Invalid state" |
code |
Embed implementation code | ```scala ... ``` |
Entity-Specific Statements¶
These statements are only valid within Entity handlers:
| Statement | Description | Example |
|---|---|---|
morph |
Change entity to a different state | morph entity X to state Y with record Z() |
become |
Switch entity to a different handler | become entity X to handler Y |
Value Expressions¶
RIDDL 2.0 introduces a real value-expression system beneath the statements. Wherever a statement needs a value, any of these forms is accepted:
| Form | Syntax | Meaning |
|---|---|---|
| Literal | "some text" |
Opaque pseudo-code, or a literal constant |
| Value reference | order.total |
A field, state field, function input, or let local |
| Constructor | OrderPlaced(id, total = x) |
Builds a message or record |
| Get | get from input SignupForm |
Reads a UI input or an entity state |
| Call | call function Pricing.Total(a, b) |
Invokes a pure function for its result |
| Prompt | prompt("compute the discount") |
A value computed by AI at generation time |
| Boolean | a > b and not c |
A structured boolean expression |
Constructors¶
A constructor builds a message or record inline. Arguments are positional first, then named, and are checked against the target's fields for count, name, order and (best effort) type:
Boolean Expressions¶
Precedence runs or < and < not < comparison < atom, with parentheses to
group. and, or, not, true and false are context-sensitive: they
are recognized only inside a boolean expression, so they stay legal identifiers
everywhere else.
Comparisons are type-safe and reference-only
Both operands of a comparison must be a typed reference — a value
reference, a get from, or a named constant. A literal is
not permitted, and this is enforced at parse time:
Name the threshold instead:
This removes magic constants from models and makes every comparison check
the types on both sides. == and != require operands of the same
category; <, >, <= and >= require an ordered numeric type.
Statement Details¶
When Statement¶
when order.isPaid and not order.isCancelled then {
send event LoginSucceeded to outlet Events
} else {
error "Authentication failed"
} end
The end keyword is required. A condition may be:
- a boolean expression:
when a > b and not c then - a bare boolean-typed reference, including a dotted path:
when order.isPaid then - a
letbinding, optionally negated:when authorized then,when !authorized then - an AI-evaluated prompt:
when prompt("the user is authenticated") then
A bare string condition is deprecated
when "the user is authenticated" then parses but draws a [deprecated]
message; write when prompt("…"). Everywhere else a bare string is a
literal, while prompt(…) marks a value an AI decides — and a
natural-language condition is the latter.
A bare reference is resolved and checked to be Boolean-typed; a clearly non-Boolean condition is an Error.
Match Statement¶
match order.status {
case Pending {
tell command ProcessOrder to entity OrderProcessor
}
case Shipped when order.isPaid {
send event OrderShipped to outlet Events
}
case >= HighValueThreshold {
tell command EscalateReview to entity Review
}
default {
error "Unknown order status"
}
}
The subject is a value reference, a get from, or a legacy pseudo-code
literal. A case pattern is one of:
- Type case — a bare type reference matching an alternant, enumerator or
message subtype:
case Pending - Comparison — an operator and a comparand, with the subject as the
implicit left operand:
case >= HighValueThreshold - Literal — a legacy pseudo-code label:
case "pending"
Each case may carry an optional when <boolean> guard. Naming an unknown
type-case is an Error. For a closed subject — an Enumeration or
Alternation — a non-exhaustive match without default draws a
StyleWarning.
Foreach Statement¶
RIDDL's only loop, and deliberately bounded — there is no unbounded iteration in the language:
The collection is a field reference or a let-bound local whose type
resolves to a collection: Sequence, Set, Graph, Table, Replica, Mapping, or a
cardinality wrapper such as many or optional.
Send, Tell and Yield¶
- send — emit on one of this processor's own outlets; a connector routes it onward
- tell — deliver directly to a specific processor (point-to-point)
- yield — produce a command's or query's declared response, without needing to know the sender's identity
send event ItemAdded to outlet CartEvents
tell command ProcessPayment(orderId) to entity PaymentService
yield result CartInfo(cart.id, cart.total)
send … to inlet is deprecated
Sending directly into another processor's inlet bypasses the streaming
model — that is tell's job. The inlet form still parses and emits a
[deprecated] message.
reply is deprecated
reply is a deprecated synonym for yield, parsing to the same node.
Put and Return¶
put publishes a value to a UI output and is valid only in
application and context handlers. return produces a
function's result and is valid only in a function body.
Require and Error¶
Refusals before effects
Within any single linear statement list, every refusal (require,
error) must appear before every effect (set, morph, become,
send, tell, yield, put). Acting and then refusing would leave
partial changes behind.
Each statement list is checked independently, so each branch of a when,
match or foreach body is its own list. A refusal after an effect in the
same list is an Error.
Do Statement¶
Use do to describe business logic in natural language that will be
implemented in target code:
do "Calculate the total price including all applicable taxes, discounts,
and shipping based on the customer's location and membership tier"
The prompt statement is deprecated
do is canonical; prompt "..." emits a [deprecated] message and
prettify normalizes it.
Do not confuse it with the prompt(...) value, distinguished by its
parentheses. The statement describes an action for a human to implement;
the value denotes something AI computes.
Code Statement¶
RIDDL's deliberate escape hatch: an opaque pass-through of raw target-language source, handed to the code generator untouched.
Supported languages: scala, java, python, mojo. RIDDL does not parse
or check the contents — everything between the fences is carried through
verbatim.
code is allowed in every statement scope, including pure
function bodies and on activate / on passivate. It is also
exempt from the refusals-before-effects rule, being
neither a refusal nor an effect.
Using it opts out of the guarantees
Those exemptions are not oversights: RIDDL cannot classify opaque source,
so it declines to guess rather than guessing wrong. But the rules exist to
buy something — a provably pure function, a lifecycle clause that provably
does not emit — and a code block suspends that for as long as it lasts,
while tying the model to one target language.
Prefer do "..." to describe intent and let the
generator implement it. Reach for code when a generator genuinely cannot
express what you need.
Morph and Become (Entity Only)¶
morph entity Order to state Shipped with record ShippedData(trackingNumber)
become entity Order to handler ShippedHandler
The morph payload is a record — a bare record reference or an inline
constructor. It is not a message; a message cannot type a state.
Level of Detail¶
Statements express pseudocode in a structured but abstract way. RIDDL does not require the system model to contain implementation code. The objectives are:
- Converting specifications to executable code should be done by humans or AI
- Statements capture interactions between model definitions
- Statements are intentionally not Turing complete —
foreachis bounded by its collection, so it does not change this - Natural language descriptions (via
do) suffice for complex logic
Applicability¶
Not all statements can be used everywhere. Availability depends on the containing definition — and, as of 2.0, several places actively ban statements:
| Context | Available Statements |
|---|---|
| All handlers | when, match, foreach, send, tell, yield, require, set, let, do, error, code |
| Entity handlers | All above + morph, become |
| Application / context handlers | All above + put |
| Functions | when, match, foreach, require, let, return, do, error, code |
on activate / on passivate |
Side-effect free only — no send, tell, yield, morph, become |
on event |
No require or error — an event has happened and must be accepted |
| Saga steps | send, tell, yield, put, do, error |
The function and lifecycle bans are enforced at parse time, so a banned statement can never enter the AST at all.
Deprecated Statements¶
| Deprecated | Replacement |
|---|---|
reply <msg> |
yield <msg> |
prompt "..." |
do "..." |
send … to inlet X |
send … to outlet Y, or tell |
Occurs In¶
Contains¶
Statements may contain:
- Value expressions (constructors, calls, gets, references, boolean expressions)
- Conditionals (in
when,matchguards, andrequire) - Literal values
- Field references
- Path identifiers to reference definitions
None of these are definitions themselves.