Continuations

When one has seen one's first Io program, and heard that Io is based on continuations, one might well wonder what a continuation is. This is easy to answer in a short,technical, sense, but for most people it's not very easy at all to actually grasp. If you already know what continuations are, this section might still provide a nice introduction to how Io uses them.

Put simply, a continuation is a representation of the rest of the program from a certain point, neatly wrapped up so that it can be called, and the rest of the program thereby resumed, at any time. One can think of continuation as a label in the program, calling the continuation being the same as issuing a goto to that label. The main difference then is that with a continuation all the variables are also restored to what they were when then the continuation was created. This means that a continuation (in Io) is a closure plus a jump destination. On top of that continuations in Io can also optionally accept parameters, in which case they are really gotos with parameters.

Don't worry if you don't understand the above immediatly, or if you don't know what closures are. I will try to explain what you need, and understanding closures, while not very hard, isn't necessary in any way.

Introducing an Example

A first step towards understanding continuations can be taken by understanding how they can be used like functions, and how calling and returning from functions can be viewed as essentially the same thing. The code fragment below is meant to look like a piece of code in an imaginary imperative language:
01  function g(n):
02    y = n / 2;
03    return(y);
04
05  z = 1;
06  x = g(8);
07  print(x + z);
This program will calculate the number 5 and print it on the screen, in a slightly roundabout fashion. We will now, step by step, walk through this small program, and piece by piece understand and convert it into a form in which we represent it as an example of continuation manipulation.

The program starts executing at line 5, assigning the value 1 to the variable z. Or, if we break this operation into even smaller pieces, it first evaluates the constant expression on the right hand side of the equal sign to the value 1, then assigns this value to the variable z. This way of expressing things is important so we'll write it down, but replace the word then with a semi colon.

evaluate the expression 1; assign the result to z
We can also express this like this:
1; -> z
The right arrow is read as assign to. This notation looks much like the original program but stresses the order in which things are executed. Next is how the entire program will look using this notation:
01  declare g(n):
02    n / 2; -> y;
03    return(y);
04
05  1; -> z;
06  g(8); -> x;
07  print(x + z);

The Return Symmetry

Now it's time to turn attention towards the function call at line 6. It calls g() defined at line 1 with the parameter 8. After the call the function will execute line 2 and then line 3, which returns control to the second half of line 6. This how functions always work: one calls them, they do some work, and then they return to the point where they were called from, often with some value. To call the function is then a lot like jumping to the first line of the function, often with a parameter value. To return is a lot like jumping back to where the function was called from, often with a result value.

So, to call a function you first need it available in form of it's name. In this case the function is called g and we call it with g(8). To be able to return from a function is a bit trickier. You need have a way to identify the place the function should return to, which doesn't have a name. So let's give this place a name in g so that we can jump back, let's call this place return. So now return is the name of the place to where the function should jump back after it has done it's deal. And on line 3, it does. Notice how this call to the place return looks exactly like the call g on line 6. This is what I like to call the return symmetry, returns are exactly like calls, they are both jumps.

To stress this g is rewritten slightly so that it shows that it's really a jump destination where the first thing that happens is that n is assigned the parameter value of the call:

declare g:
  -> n;
  n / 2; -> y;
  return(y);
Notice how the start of g now looks exactly like the second half of line 6, where the jump to return takes the program. From here on we will write the assignment to n on the same line as the function declaration to stress that the assignment constitutes the parameters of g.

Where to Return To, an Extra Parameter

An important thing to note now is that while g is always the same place, the start of the function by that name, return is a different place each time the function is called. If we called g again on line 8 we wouldn't want it to return to line 6 that time. So how does the computer know where the return place is each time? The same way it gets a different n each time! It sends the place the function should return to as as an implicit, hidden parameter to the function. So when you think g takes one parameter, n, it really takes two parameters, n and return. Let's rewrite our definition of g to make this explicit:
declare g: -> n return;
  n / 2; -> y;
  return(y);
Now, this is starting to look a lot like Io code. In fact, the only differences between this code and Io code are syntactical. To transform this into Io code we need to change the calling syntax, until now we have used the classic mathematical syntax f(a,b,c), in Io the brackets are omitted and the same call is written f a b c. Furthermore, this code infix operators, the /, for division, and the /, for addition. Infix operators aren't available in Io and the code has to be rewritten as a regular function call instead. Also, for reason that will become clearer later, the call to print on line 7 will need to be rewritten with a new temporary to hold the result of the addition. Declarations in Io are terminated by a period (.) and the last statement needs no semicolon:
01  declare g: -> n return;
02    / n 2; -> y;
03    return y.
04
05  1; -> z;
06  g 8; -> x;
07  + x z -> t;
07  print t