Code blocks (was Re: Proposal: opt-out local scoping)

# Yuh-Ruey Chen (17 years ago)

Mark S. Miller wrote:

Since ES is not an expression language, it's useful to divide the question into

  • TC for expressions expr equiv (function(){return expr;})() ES violations: this, arguments. 'this' breakage can possibly be addressed with '.bind(this)' as in my previous message. expr equiv (function(){return expr;}).call(this)

  • TC for blocks {stats} equiv (function(){stats})(); Same issues re this, arguments Additional ES violations: return, break, continue, var hoisting

Note: The above litany of problems are not fixable in ES. But we should try to avoid digging this hole any deeper.

Any procedural language will have issues with TC then. The only longterm solution to this that I can think of is to add Ruby-like code blocks to the language, which inherit this, arguments, var scope, and return/break/continue continuations.

Not ever having dealt with implementing continuations, I have no idea how simple it would be to add code block semantics to the language.

There are two styles to code blocks that I know of, which are not mutually exclusive:

  1. adding a new type of function
  2. denoting that a parameter to a function is a block, and treating any function passed in as that parameter to be a block

In either style, the function artifacts (this, arguments, etc.) would be determined lexically.

The first style would require some new syntax to define blocks. Redefining |this| via function.call and function.apply should also be disallowed. We have several options to choose from. Some examples:

Ruby-like: do(...) { ... } do { ... }

Usage of Java keywords: function(...) inherits { ... }

Some obscure yet convenient syntax: #(...) { ... }

The second style would require some sort of block annotation to a parameter, e.g.

function(x: block} { ... }
function(block x) { ... }
function(&x) { ... }

One advantage of the second style is that we can add new types of control abstractions relatively seamlessly with some more Ruby-like sugar. If the last parameter to a function is annotated to be a block, then the following:

function control(..., block f) { ... }

control(...) statement

would pass in a 0-parameter code block composed of |statement| (which could be a {...} block) to the function |control| as the parameter f. Example:

function read(openArg, block f) {
    try {
       this.open(openArg);
       f();
    } finally {
       this.close();
    }
}

FileInputStream.prototype.read = read;

function findInFiles(files, x) {
    for each (let file in files) {
        input = new FileInputStream();
        input.read(file) {
            let lineNum = 0;
            for (let line in input.readLines()) {
                if (line == x)
                    return [file, lineNum];
                lineNum++;
            }
        }
    }
}

Hope that all made sense.

-Yuh-Ruey Chen