Recently I've read about Lua. It's a nice simple language, with a syntax that's quite close to my ideal for a dynamically-typed imperative language. One of the things it omits for simplicity's sake is first-class continuations. I've been wondering how one might specify a similar syntax if you have a Schemeish infrastructure, so that the usual control structures can be desugared into functions, conditionals, and tail-calls. I also have in mind Modula-3's definition of some control structures in terms of exceptions. In the following I'm not going to talk about the whole syntax; I'll omit things which don't involve blocks.

The basic form of a block expression is:

name(arguments): do statements end

The value of this expression is a function which takes some arguments and which when called executes the statements. The scope of its name is just within the block itself; it is used in inner scopes to refer to shadowed variables in outer scopes.

To execute the block immediately, the arguments can be omitted:

name: do statements; end

is equivalent to

name(): do statements; end()

that is, it specifies a function that has no arguments and which is called immediately.

The name can also be omitted. Doing so just means there's no way to refer to this block's variables if they are shadowed in an inner scope. For example, an anonymous function (lambda expression) is typically

(args): do statements; end

You might also want to allow a shorter version of

(args): do return(values); end

for use in expressions, perhaps

(args): return (values)

A minimal block like the following is just like a Lua block:

do statements; end

Just before the end of each block there's an implicit return(nil). You can return from a block early with whatever value or list of values you want.

Instead of do...end you can write a conditional:

if expression then true statements else false statements end

If the else part is omitted then the false statements are implicitly return(nil). You can also add elsif expression then alternate statements parts in the usual way.

The basic form of a loop is

name: do statements loop

which is equivalent to

name: do statements return(name()) end

that is, loops are implemented using tail recursion. (Loops do not have to be named, but I've written the desugaring in terms of a named block to avoid having to talk about hypothetical unique names.)

name: while expression do statements loop

is equivalent to

name: if expression then statements return(name()) end

and

name: do statements while expression loop

is equivalent to

name: do statements if expression then return(name()) end end

You can use until expression instead of while not(expression).

Given the above, you can see that return inside a loop is similar to break in C. It's often nice to be able to break out of more than one nested loop at a time, or return from a function inside a loop. This is where a block's name comes into play. To return from a named outer block, call name.return. For example:

def bsearch(arr, val): do def lo, hi := 1, #arr while lo <= hi do def mid := math.floor((lo + hi) / 2); if arr[mid] == val then bsearch.return(mid) elsif arr[mid] < val then lo := mid + 1 else hi := mid - 1 end loop end

A bare return in the above would specify the value of the if block, not the function.

This kind of return makes it easy to write call-with-current-continuation:

def callcc(f): do def cont(...): do callcc.return(...) end return(f(cont)) end

However this is unnecessary because return is already an expression whose value is the current continuation. It's just like other function arguments, except that it's implicit (a bit like the self argument in Lua's methods) and can't be assigned to (so that the compiler can do tail-call optimisation). (Actually, what if you *could* assign to return? Eeeeevil!)

There are three forms of statement within a block: definitions, assignments, and function calls. Since a block without arguments is equivalent to a function expression that is immediately called, non-function blocks can be statements. A function definition

def name(arguments): do ...

is equivalent to

def name := name(arguments): do ...

For bigger code, it's useful to allow a block's name to be repeated after its end or loop, for example:

def long_function(arguments): do code code code end long_function

You can further desugar definitions, since

old_name: do before statements def variables := values after statements end

is equivalent to

outer_name: do before statements inner_name(variables): do after statements end(values) end

except that you need to fix up the after statements so that unqualified returns are qualified with the outer_name, and variables qualified with the old_name are qualified with the outer_name or inner_name as necessary.

Some things are still missing from this description. A for loop is necessary, probably based on some kind of iterator concept, but that's too far away from the topic of this post to be worth pursuing in detail now. Also, we would probably like a standard exception mechanism; this is not trivial to implement in terms of continuations because the code that handles an exception is determined dynamically (like Perl local) whereas continuations are lexical (like Perl my). Finally, the combination of continuations and concurrency is utterly filthy: imagine what happens if one thread calls a continuation created by another...