next up previous
Next: References Up: A slice of Pizza: Previous: Algebraic data types

Tail calls

A tail call is one that occurs as the very last step in a method. A clever compiler can implement tail calls without growing the stack; instead the current call frame is replaced by the frame for the tail call. The Java Virtual Machine does not support tail calls, but they can be simulated. (This is pretty ironic, considering that tail calls were popularised by Guy Steele, one of the three principal designers of Java [SS76].) The simulation is expensive in time: in Pizza, tail calls cost an order of magnitude more than ordinary calls. But since tail calls don't add to the stack, they can be nested arbitrarily deep.

For instance, one can simulate a finite-state machine by defining method for each state, which calls the method for the next state with a tail call. This can be very flexible: you don't need to decide in advance what different states exist, you just define one method for each state as required. To achieve the same effect without tail calls, you need to have each routine return an object that contains the next method to be called, which is how tail calls are implemented in Pizza.

Tail calls are indicated by placing goto in front of the call. Methods that may contain a tail call or be the target of a tail call are indicated by writing continue in the declaration. (Both goto and continue are already reserved words in Java.)

As an example, here is a routine that processes text, counting for each pair of parentheses the number of items (separated by commas) between them, and signalling an error if any item is empty or if parentheses are nested or do not match.





Example 5.1 Tail calls in Pizza.

class Counter {
  final char EOF = (char)0;
  InputStream in;
  Counter (InputStream i) { in = i; }
  char next () {
    int c = in.read();
    if (c == -1) return EOF; else return (char)c;
  }
  continue void normal () {
    switch (next()) {
      case EOF: goto done ();
      case '(': goto startitem (0);
      default: goto normal ();
    }
  }
  continue void startitem (int count) {
    switch (next()) {
      case EOF: goto error ();
      case ')': goto error ();
      case ',': goto error ();
      default: goto insideitem (count);
    }
  }
  continue void insideitem (int count) {
    switch (next()) {
      case EOF: goto error ();
      case ')': System.out.println("count " + count); goto normal ();
      case ',': goto startitem (count+1);
      default: goto insideitem (count);
    }
  }
  continue void done {
    System.out.println("done");  return;
  }
  contine void error {
    System.out.println("error");  return;       
  }
  static public void main (String[] args);
    new Counter(System.in).normal();
  }
}







next up previous
Next: References Up: A slice of Pizza: Previous: Algebraic data types
Philip Wadler