Lambda vs. function
Dave Herman
dherman at ccs.neu.edu
Thu Oct 16 14:39:49 PDT 2008
>> b) creating a clearer place in the language syntax to enforce tail
>> calling by eliminating `return'
>
> I don't understand what you mean in point b.
Consider two different syntaxes for a function performing a tail call:
lambda(x) { f(x) }
function(x) { return f(x) }
The traditional semantics of `return' is 1) evaluate the argument to get
a value and 2) jump to the caller (i.e., pop the stack frame) with that
value. If we try to graft tail calls on top of `return', we reverse the
semantics: 1) jump to the caller with an unevaluated expression and 2)
evaluate the subexpression.
But what if the `return' occurs in a try-block? Then popping the whole
activation frame would be incorrect (it would unwind the exception
handler too soon). So now we have to say the semantics of `return'
depends on its context:
1) if the `return' is in tail position, then
1.1) jump to the caller and
1.2) evaluate the subexpression
2) if the `return' is not in tail position, then
1.1) evaluate the subexpression and
1.2) jump to the caller
You could certainly argue that this is just a syntactic difference for
the same behavior as the `return'-less form. But `return' suggests a
control effect, and imperative behavior, and we're messing with its
interpretation.
I claim that `return' suggests that a function call corresponds to an
activation frame (which is in fact the way ES3 is explicitly specified),
which is popped after evaluating the argument of a `return' statement.
By contrast, a procedure form that does not use `return' simply avoids
any suggestion of control effect, and tail calling is a natural
interpretation.
To some degree, you could wedge proper tail calling on top of the
semantics of function/return. But the notion of what constitutes a tail
position is much clearer with lambda.
> Why isn't function a realistic alternative? You cited four differences:
>
> return: Return from within a function returns from that function
> instead of doing a longjmp. I'd consider that to be a feature.
There is no longjmp in ES, nor is longjmp being proposed. Maybe you're
mixing semantics with implementation. Or maybe you're making fun of the
return-to-label proposal. I don't see what you're driving at. But
`return' *is* a transfer of control; it is a jump. It just happens to be
a jump that you don't have to implement in C with longjmp.
> this: You have a point there.
>
> var: Not an issue if you're not using var inside the lambda. Code
> generation that wants to use lambda would use var why?
>
> arguments: Again, not an issue if you're not using the arguments
> array. If a code generator is updated for lambda, then presumably it
> will be updated not to use the arguments array.
If you're writing a compiler that targets ES, you can have conventions
that avoid using those features. But for defining ES language features
by desugaring, we don't have that luxury; for example:
let (x = e) { s } !== (function(x) { s })(e)
because `s' may contain any of those four features (this, var,
arguments, return). This would be a problem for macros, too.
Dave
More information about the Es-discuss
mailing list