if, select and loops
Use a condition to choose, a loop to repeat, and a named transfer to make the destination clear. These snippets belong inside application actions; their inputs and helper actions come from the surrounding application.
if: choose a branch or return early
if accountIsBlocked then
return "Blocked"
else if amount > 5000 then
return "Needs review"
else
return "Ready"
end if
Conditions are Boolean. Only the chosen branch runs; return completes the enclosing action.
if(condition, yes, no): choose a value
let label = if(needsReview, "Waiting for review", "Ready")
Only the selected result is evaluated. The two-argument form if(condition, value) returns nothing when unselected; it is not null coalescing.
select / case: choose the first matching case
select sourceName
case "customers", "contacts"
ImportContacts()
case "orders"
ImportOrders()
else
throw new Global.System.ArgumentException("Unknown import", "sourceName")
end select
The subject is evaluated once. Cases are considered in order, with no fall-through. Ordinary String matching is case-insensitive.
select / take: produce a value from a case
let fee as Decimal = select amount
case <= 100
take 2
case <= 1000
take 5
else
take 10
end select
Take supplies the selected value; return would exit the action. Every normally completing selected path needs a result.
for_each: visit items in a sequence
for_each address in addresses
if address is nothing then
continue
end if
let label = address.Trim()
if label is empty then
continue
end if
SendWelcome(label)
end for_each
Each item is visited in source order. An empty source runs no iterations. For an IAsyncEnumerable source, ordinary for_each selects asynchronous enumeration without a separate await for_each spelling.
break / continue / exit: control the current loop
for_each item in items
if item.IsInvalid then
continue
end if
if item.IsCritical then
break
end if
Process(item)
end for_each
Continue proceeds to the next iteration; break ends the innermost loop. Bare exit means the same as break. In sequential nesting, exit while, exit repeat and exit for leave the nearest loop of that kind; exit for includes for_each. Required cleanup still runs.
for_each / range / step: iterate numeric bounds
for_each i in range 10 to 0 step -2
ProcessNumber(i)
end for_each
To is inclusive: this visits 10, 8, 6, 4, 2 and 0. Without step, direction follows the endpoints. The iteration variable is readonly. Numeric iteration uses for_each in range rather than a separate for i = header.
where / order_by / skip / take: shape a loop's input
for_each customer in customers
where customer.IsActive
order_by customer.Name
skip 20
take 10
ProcessCustomer(customer)
end for_each
The clause order is filter, order, skip, take. These clauses shape the sequence before body entry; they do not mean select's result-producing take.
while: test before each iteration
while remaining > 0
ProcessBatch()
remaining -= batchSize
end while
A false initial condition runs no iterations. The surrounding action must ensure a positive batchSize so this loop makes progress.
repeat / until: run before testing
repeat
ProcessBatch()
remaining -= batchSize
until remaining <= 0
The body runs at least once. Until stops when true; a trailing while continues while true. The condition closes the loop, with no following end repeat. Continue still reaches that condition.
repeat times / forever: repeat explicitly
repeat 3 times
RefreshCache()
end repeat
repeat forever
ProcessNextRequest()
end repeat
Counted and forever forms close with end repeat. A nonpositive count runs no iterations. Use break or a named loop transfer when the body should stop an unbounded loop.
with name / break / continue: target an enclosing loop
for_each customer in customers with name: customers_loop
for_each order in customer.Orders
if order.IsInvalid then
continue customers_loop
end if
if order.IsCritical then
break customers_loop
end if
ProcessOrder(order)
end for_each
end for_each
The named continue moves to the next customer; the named break ends customer processing. Names are case-insensitive. The clause also applies to the other approved loop forms. It does not widen concurrent iteration or callable boundaries.
CurrentLoop / LastLoop: inspect loop progress
for_each order in orders
ProcessOrder(order)
end for_each
Global.System.Console.WriteLine("Processed {{LastLoop.NumberOfIterations}} orders")
CurrentLoop describes the innermost executing loop. After a loop completes, LastLoop describes that loop within its containing lexical block. LastLoop.TerminatedByExit distinguishes explicit termination from normal exhaustion or condition completion. See the metadata rules for available fields and lifetimes.
mark / goto: name and jump to a destination
let remaining = 3
mark next_attempt
if TrySend() then
goto sent
end if
remaining -= 1
if remaining > 0 then
goto next_attempt
end if
return false
mark sent
return true
Goto supports forward and backward jumps to marks. It is ordinary control flow, not structured retry. A mark is not a loop name; jumps cannot bypass initialization, cleanup or concurrency obligations. Read the destination rules.
For full selection, scope and concurrent-loop behavior, use the control-flow specification.
Previous: Values and operators · Next: Actions and callables