Why:

How:

Add four productions to the grammar:

Expression  ::= ... | lambda Formals Block
                    | lambda Formals Expression
Declaration ::= ... | lambda Identifier Formals Block
                    | lambda Identifier Formals Expression

Or possibly just one single production:

Expression ::= ... | lambda Formals Statement

This would allow for an expression body since ExpressionStatement is a production of Statement. Unsure whether this causes any insurmountable ambiguities.

What:

Examples:

Several functional styles using lambda:

lambda fact(n) {
    if (n === 0 || n === 1)
        1
    else
        n * (fact(n-1))
}
 
lambda factIter(n, acc) {
    if (n === 0 || n === 1)
        acc
    else
        factIter(n - 1, n * acc)
}
 
lambda factCPS(n, k) {
    if (n === 0 || n === 1)
        k(1)
    else
        factCPS(n - 1, lambda(result) k(n * result))
}

Desugaring the function expression function(x1,...,xn) Body requires simulating the behavior of this, arguments, and return. The latter takes advantage of return to label, but could also be implemented with exceptions. Since lambdas and functions are called with the same calling mechanism, we can’t just pass the value for this as a parameter. So the function call protocol saves the this value in a variable thisRegister. The implementation of arguments is described below.

lambda(x0,...,xn,...$rest) {
    let $THIS = thisRegister;
    let arguments = makeAlias([[lambda() x1, lambda($x1) x1 = $x1],
                               ...,
                               [lambda() xn, lambda($xn) xn = $xn]],
                              $rest);
    $RETURN: { Body; void 0 }
}

Then return e desugars to return : $RETURN e and this desugars to $THIS. Note that both $RETURN and $THIS should be gensymed once for the whole program, i.e., they should not be capturable by user code but should be the same in every instance of the desugaring.

The thisRegister variable is set by the function-call protocol. A function call e.m(e1,...,en) desugars to

let ($obj = e, $t1 = e1, ..., $tn = en, $method = $obj.m)
  (thisRegister = $obj, $method($t1, ..., tn))

Notice that thisRegister is trashed as the very last action — even after the method dereference, in case the dereference invokes setter code — before calling the method.

The makeAlias helper produces an array-like object whose indexed properties behave like aliases for the function parameters. Its implementation looks more or less like so:

lambda makeAlias(params, rest)
  ({ get *: lambda(i) {
                if (!isNumeric(i))
                    // etc.
                else if (i < 0)
                    // etc.
                else if (i < params.length)
                    params[i][0]()
                else {
                    i -= params.length;
                    if (i < rest.length)
                        rest[i]
                    else
                        // etc.
                }
            },
     set *: lambda(i, x) {
                if (!isNumeric(i))
                    // etc.
                else if (i < 0)
                    // etc.
                else if (i < params.length)
                    params[i][1](x)
                else {
                    i -= params.length;
                    if (i < rest.length)
                        rest[i] = x
                    else
                        // etc.
                }
            }
   })

Issues:

 
strawman/lambdas.txt · Last modified: 2008/12/05 02:22 by brendan
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki