How would shallow generators compose with lambda?
Jim Blandy
jimb at mozilla.com
Thu May 28 11:48:56 PDT 2009
On 05/28/2009 11:08 AM, Brendan Eich wrote:
> On May 14, 2009, at 2:10 PM, Mark S. Miller wrote:
>
>> On Thu, May 14, 2009 at 1:22 PM, Brendan Eich <brendan at mozilla.com>
>> wrote:
>>> On May 14, 2009, at 12:24 PM, Jason Orendorff wrote:
>>>> 3. When a lambda yields, [...]
>>>> there may be other functions on the stack, in between. You can't
>>>> always statically tell which ones. This means that generator
>>>> semantics affect the integrity of code that isn't in a generator.
>>>
>>> [...] this extends the finally integrity
>>> degradation outside of the lexical scope of the generator function.
>>> Good
>>> point.
>>>
>>>> [...] with generators+lambdas, almost any function call *anywhere*
>>>> in the
>>>> program might never return or throw. This weakens 'finally', at
>>>> least.
>>> [...]
>>
>>> function gen(arg) {
>>> foo((lambda (x) yield x), arg);
>>> }
>>> function foo(callback, arg) {
>>> try {
>>> callback(arg);
>>> } finally {
>>> alert("I'm ok!");
>>> }
>>> }
>>> g = gen(42);
>>> print(g.next()); // tell the user the meaning of life, etc.
>>> g = null;
>>> gc();
>>
>>
>> Thanks all, this has been very clarifying. You both have put your
>> finger on what was nagging at me and explained it clearly.
>
> I think we missed an alternative that comports with Tennent's Oversold
> Correspondence Principle, *and* composes. Thanks to Dave Herman for
> pointing it out.
>
> function gen(x) {
> foo( lambda (x) (yield x*x) );
> }
>
> need not yield from gen if the lambda is called from foo or another
> function -- it can throw the same error it would throw if the lambda
> escaped upward/heapward and was called after gen had returned. There's
> no requirement that yield not throw in any case where the lambda is
> not applied in the context of gen.
"Not applied in the context of gen" means what, exactly? Called
directly from gen? Called only by lambdas enclosed by gen? Called in
some gen's dynamic scope?
Would yield need to work normally in this case?
function f(x) {
((lambda (g, h, x) { return g(g, h, x); })
(lambda (g, h, x) {
if (x > 0)
return g(g, h, x-1);
else
return h(x);
},
lambda (x) { yield 42; },
x));
}
f(10);
Nothing there but local lambdas. If lambda isn't transparent in such
cases, then its value as something to safely desugar to is pretty weak.
If yield does work, then we're capturing deep stacks. A more plausible
example:
function j(x) {
(lambda (y) {
(lambda (z) {
yield z;
}
(y + " and a dyne"));
}
(x + ", a poundal"));
}
print(j("I love you").next());
This is just a desugaring of some nested lets, but we still have yield
capturing many frames. If this doesn't work, lambda is really useless.
For what it's worth, speaking as a long-time Scheme fan, I wouldn't add
lambda to ES. It seems too similar to function; there will be endless
blog posts explaining the differences and motivation, mostly slightly
wrong. The best ones will say, "Don't use it; just use function." And
lambda introduces an awful lot of subtlety for something whose main
claim to utility would be in allowing precise and clear definitions of
new control constructs through desugaring. Natural language is bad, but
not this bad.
More information about the es-discuss
mailing list