Branching, loops, exceptions, and scope.
The if() method can be used in the form:
if(<condition>, <do message>, <else do message>)Example:
if(a == 10, "a is 10" print)The else argument is optional. The condition is considered false if the condition expression evaluates to false or nil, and true otherwise.
The result of the evaluated message is returned, so:
if(y < 10, x := y, x := 0)is the same as:
x := if(y < 10, y, 0)Conditions can also be used in this form:
if(y < 10) then(x := y) else(x := 2)elseif() is supported:
if(y < 10) then(x := y) elseif(
y == 11) then(x := 0) else(x := 2)Also supported are Smalltalk style ifTrue, ifFalse, ifNil and ifNonNil methods:
(y < 10) ifTrue(x := y) ifFalse(x := 2)Notice that the condition expression must have parenthesis surrounding it.
The loop method can be used for "infinite" loops:
loop("foo" println)The Number repeat method can be used to repeat a loop a given number of times.
3 repeat("foo" print)
==> foofoofooArguments:
while(<condition>, <do message>)Example:
a := 1
while(a < 10,
a print
a = a + 1
)Arguments:
for(<counter>, <start>, <end>,
<optional step>, <do message>)The start and end messages are only evaluated once, when the loop starts. Example:
for(a, 0, 10,
a println
)Example with a step:
for(x, 0, 10, 3, x println)Which would print:
0
3
6
9To reverse the order of the loop, add a negative step:
for(a, 10, 0, -1, a println)Note: the first value will be the first value of the loop variable and the last will be the last value on the final pass through the loop. So a loop of 1 to 10 will loop 10 times and a loop of 0 to 10 will loop 11 times.
loop, repeat, while and for support the break and continue methods. Example:
for(i, 1, 10,
if(i == 3, continue)
if(i == 7, break)
i print
)Outputs:
12456Any part of a block can return immediately using the return method. Example:
Io> test := method(123 print;
return "abc"; 456 print)
Io> test
123
==> abcInternally, break, continue, and return are handled by the iterative evaluator's frame state machine: each sets a non-local control signal on the current frame, and the eval loop unwinds frames until it reaches the enclosing loop or block.
There are singletons for true, false and nil. nil is typically used to indicate an unset or missing value.
The comparison methods:
==, !=, >=, <=, >, <return either the true or false. The compare() method is used to implement the comparison methods and returns -1, 0 or 1 which mean less-than, equal-to or greater-than, respectively.
An exception can be raised by calling raise() on an exception proto.
Exception raise("generic foo exception")To catch an exception, the try() method of the Object proto is used. try() will catch any exceptions that occur within it and return the caught exception or nil if no exception is caught.
e := try(<doMessage>)To catch a particular exception, the Exception catch() method can be used. Example:
e := try(
// ...
)e catch(Exception,
writeln(e coroutine backtraceString)
)The first argument to catch indicates which types of exceptions will be caught. catch() returns the exception if it doesn't match and nil if it does.
To re-raise an exception caught by try(), use the pass method. This is useful to pass the exception up to the next outer exception handler, usually after all catches failed to match the type of the current exception:
e := try(
// ...
)e catch(Error,
// ...
) catch(Exception,
// ...
) passCustom exception types can be implemented by simply cloning an existing Exception type:
MyErrorType := Error cloneBecause evaluation state lives in heap-allocated frames, the frame that raised an exception is still a live, inspectable object when a handler runs. A handler can choose to resume the computation at the point of the raise — returning a value in place of the exception — instead of unwinding past it. This enables Smalltalk- or Common-Lisp-style condition handling where the handler decides whether to retry, substitute a value, or abort.
The iterative evaluator makes first-class continuations straightforward: callcc snapshots the current frame chain as an ordinary Io object. That object can be stored, invoked later, or — because it's just a graph of Io values — serialized and resumed in another process.
callcc is not exposed in the top-level Lobby because it's easy to misuse. Where continuations are needed, they're reachable through the VM's reflective interface.