Two small features carry more everyday code than most big ones. The first closes the gap between declaring a name and giving it meaning. The second is the answer to a question every Pascal newcomer asks in week one — where is return? — and the answer is better than the thing it replaces.

The example is InitAndLeave.

A declaration that starts alive

const
    Base = 40;

var
    answer : int64 := Base + 2;      { unit level: constant expressions }

function FirstDivisor(n : int64) : int64;
var
    d : int64 := 2;                  { local: any visible expression }

A local initializer chains anything the position can see — the parameter, constants, earlier locals, a call result. A unit-level initializer must be a constant expression, and the compiler explains why in the refusal:

analyzer error 5231: the initializer of program-level variable 'n' must be
a constant expression: program-level state is materialized before the body
runs, so there is no earlier moment for run-time code

No reader ever meets d without knowing what it starts as; no init-order puzzle can exist because there is no moment before the constants.

The var section is not pinned to a block’s head. It is legal wherever a statement is legal, and it declares into the compound statement that encloses it — a begin-end compound, a repeat-until body, and a concurrent block all read it the same way:

begin
    var scaled : int64 := answer * 2;    { statement position: a run-time
                                           expression is fine here }

    for i := 1 to 3 do
    begin
        var fresh : int64 := 100;        { this compound's own variable }

        fresh := fresh + i;              { 101, 102, 103 — fresh each round }
    end;
end;

Three consequences follow from that one scope rule, and each is visible in the example’s output:

  • The home is the enclosing compound, not the subprogram. A variable declared in a loop body belongs to that body: the enclosing code cannot see it, and the same name in two sibling compounds is two variables.
  • The initializer’s store runs at the written line, on every entry. A loop body’s initialized variable starts fresh each round — the store re-runs when control passes the declaration — and a statement standing before the declaration in the same compound still reads the zeroed default the storage was born with.
  • A run-time initializer is legal in statement position, even in the program body. The constant-expression rule above binds the block-head program declarations, which are materialized before the body runs; a statement-position store runs mid-body, where any visible expression is as sound as any assignment. Reading a variable declared after your own is still refused, in the same words the block-head rule uses.

One var in statement position reads a single declaration group, so the statement that follows it is never mistaken for a second group.

leave: the activation exit

Mica has no return, and the difference is not spelling. A function’s result is its own name — assigned like a variable, kept until the activation ends — and leave ends the activation:

FirstDivisor := n;                   { the prime's answer: itself }
while d * d <= n do
begin
    if n mod d = 0 then
    begin
        FirstDivisor := d;
        leave;                       { the activation ends HERE }
    end;
    d := d + 1;
end;

Two consequences. Result-setting and exiting are separate acts, so “the answer unless something better turns up” costs one assignment before the loop instead of a flag. And bare leave inside a loop still exits the activation, not the loop — one word, one meaning, with every pending defer running on the way out, which is why on fail leave could reuse the whole mechanism.

when puts the condition on the exit itself, which turns the guard clause into one line with no pyramid above the real body:

procedure Report(n : int64);
begin
    leave when n = 0;
    WriteLn("  first divisor of %lld is %lld", n, FirstDivisor(n));
end;
The guard clause
  first divisor of 91 is 7
  first divisor of 97 is 97

Leaving a loop needs the loop’s name

To exit a loop early, name it — and leave <label> when exits it from any depth, in one line:

search : for row := 1 to 5 do
    for col := 1 to 5 do
    begin
        tries := tries + 1;
        found := row * 10 + col;
        leave search when row * col = 12;
    end;
Leaving a labeled loop from inside
  product 12 first at row 3 col 4, after 14 tries

Fourteen tries instead of twenty-five — and no break/break 2/flag ceremony. A name that names no enclosing loop is refused with the scope rule attached:

analyzer error 5227: no enclosing loop is named 'hunt': a loop name can
only be targeted from inside the loop it names

next is the family’s small sibling — skip to the loop’s next round:

for i := 1 to 10 do
begin
    if i mod 2 = 0 then
        next;
    odds := odds + i;
end;
Skipping with next
  the odd numbers below eleven sum to 25

What this does not do

  • leave carries no value. The result name already has one; the exit is an exit, not an expression. That separation is what let the failure channel, cancellation, and defer all ride the same machinery.
  • Bare leave never means “exit the loop”. In a deeply nested body that reads surprisingly at first and exactly once — after that, every leave in any code means one thing.
  • A deferred statement cannot leave. Cleanups run inside the exit sequence; an exit inside the exit is refused — defer’s rules hold here unchanged.

Try it

git clone https://gitlab.com/mica-lang/mica-container.git
make -C mica-container/examples/InitAndLeave run

Rename the label to hunt in the leave line only and meet 5227. Then move FirstDivisor := n; below the loop and watch the prime rows change — the assignment-then-leave shape made visible by breaking it.

Next

With maps, bit records and this pair, the retired language tour’s lessons all have focused homes. The series’ remaining ground: the C boundary, and tooling end to end.