Refingere has three statements that decide what runs: if, for and match. There's no while loop, since for covers it. This page covers if and for, and the statements that leave a loop or a function early. match has a page of its own, Pattern Matching.
if takes a condition, then, and a branch. else and a second branch are optional. Each branch is either a single expression on the same line or a { } block. See If.
To pick between two values rather than two pieces of code, write the if where a value is wanted: if c then a else b, each branch a value - one expression, or a block whose last expression gives it - with else if chaining another. It works anywhere a value is expected, and then and else can each start a line of their own, indented to continue the statement. There's no condition ? a : b. Without an else, the value is undefined when the condition doesn't hold. See Inline If.
A block written where a value is wanted is one: its statements run in a scope of their own, so the names they bind end with the block, and its value is its last statement's - when that's an expression - or undefined. It keeps a calculation's working names out of the function's way. A return or !! inside still leaves the function. A lambda's { } body is different: it's a function's body, giving what its returns give - λ(x) ({ ... }), in parentheses, is a block used as a value. See Block.
else if chains another if onto the else, on the same line: the conditions are tried in order, and the first that holds runs its branch. A match whose cases have if guards does the same, with case _ in place of the final else - and a match can test patterns as well as conditions. Both can be a value. See Guards and A match as a value.
for runs its block once for each element of anything Iterable: lists, ranges, maps, sets, iterators, and your own types that implement it. An optional second name gets the element's position, counting from 0. Ranges are written with ..<, which leaves out the end, or ..=, which includes it. See For.
The loop's name can be a pattern, the same as a let's, to take each element apart: (name, age) for a tuple, {x, y} for a struct, [first, ...rest] for a list. entry as (name, age) keeps the whole element under a name as well. The parts are there in the body and in the loop's modifiers.
Four modifiers go between the collection and the block, so the body is left with only the work itself:
if cond - runs only the iterations where cond holds.skipping cond - leaves out the iterations where cond holds.while cond - ends the loop at the first element where cond doesn't hold.until cond - ends the loop at the first element where cond holds.Any number of them can be written, and they're checked in order for each element. They can use the loop's names and anything else in scope.
inf - or ∞, the same thing - counts 0, 1, 2, ... without end (it's #builtin.forever()). As a range's end it counts from the start instead: 1..<inf and 1..=∞ are 1, 2, 3, ... - an Iterator, so .take(n), .filter(...) and the rest work on it. With until or while it's the loop other languages write as while, and without them it runs until a break or a return.
break ends a loop, and continue moves on to its next element. Both name the loop by the variable it binds, so it's always clear which loop they mean, however deeply nested. There are no labels to add. See Break and Continue.
return ends the function with a value, and error ends it with an error - see Errors. In a generator, yield hands over a value and pauses instead of ending - see Functions.
defer schedules an expression to run when the current block ends, however it ends: at the end of the block, at a return, break or error, or when !! passes an error up. It keeps cleanup next to what it cleans up. Attributes narrow it down to some of those endings - see Resources and Memory.