Programming Language for Old Timers

by David A. Moon
February 2006 .. September 2008

Comments and criticisms to dave underscore moon atsign alum dot mit dot edu.

Previous page   Table of Contents   Next page

Why No Multiple Values?

Common Lisp, Dylan, and Go have multiple values, why doesn't PLOT have them? It used to, but I removed the feature because it created extra complexity in many places and most of the same functionality and efficiency is provided by the destructuring feature, which is more general.

Common Lisp's multiple values have three benefits:

1) You can use multiple values to get multiple results from a function without resorting to global variables. The syntax is fairly simple (although multiple-value-bind is rather a long name). PLOT gets the same functionality by returning a list and destructuring it in the caller. The syntax is simple, using the prefix [ operator to invoke the list constructor. For example:

def [a, b, c] = f(x, y)
defun f(x, y)
  [foo(x), bar(y), baz(x, y)]

2) You can use multiple values to return optional results that do not interest all callers, e.g. the Common Lisp floor function returns the remainder as a second value, the Common Lisp gethash function returns a flag indicating whether the result was present or defaulted. PLOT does not have this feature, so you have to call a function with a different name if you want the optional result. For example, PLOT dictionaries use dict[key] to get the value or contains?(dict, key) to get the entry present flag. A more efficient version of

if contains?(dict, key) then dict[key] else default
dict[key, default: default]

3) Returning multiple values does not allocate any heap memory in most Common Lisp implementations. This is usually accomplished by using a special return address to signal that the caller wants to receive multiple values, and returning the values in the same registers used to pass arguments.

A PLOT implementation can do exactly the same thing to optimize unboxed lists. Specifically, a function that returns a list of three elements can put the elements in three argument registers and jump to a runtime routine %return-unboxed-3-list. Normally this will just construct and return a list of three elements, but if the return address is the runtime routine %receive-unboxed-3-list it just jumps to the real return address, which expects to see the list elements in the argument registers. A function call expecting to receive a list of three elements and immediately destructure it stashes the real return address in a conventional register and substitutes %receive-unboxed-3-list as the return address. If control actually reaches %receive-unboxed-3-list, it verifies that the contents of the result register is a list of three elements, destructures it into the argument registers, and jumps to the real return address.

This technique provides efficient returning of multiple values, complete compatibility between the normal case and the multiple-value case, and no need for static information about function result types.

Previous page   Table of Contents   Next page