On Tue, Dec 23, 2014 at 4:26 PM, Jonathan S. Shapiro <[email protected]> wrote:
> On Tue, Dec 23, 2014 at 4:00 PM, William ML Leslie
> <[email protected]> wrote:
>>
>> On 24 December 2014 at 06:30, Geoffrey Irving <[email protected]> wrote:
>> > In python,
>> >
>> > closures = []
>> > for i in range(10):
>> > closures.append(lambda x:x+i)
>> >
>> > makes 10 copies of lambda x:x+9.
>
>
> This is timely, because Eric Northup just stopped by and we were talking
> about this case. He points out that it's a very common pattern when kicking
> off concurrent sub-programs, and it messes people up. The question is: do
> closures capture variables by value or by reference? The classic answer is
> "by reference", and the example above illustrates why this can get you in
> trouble in a language having mutable variables that also performs capture by
> reference.
>
> Eric notes further that C++ lambdas allow you to say explicitly which one
> you want, and that this has gone a long way to reduce a certain class of
> errors by letting people say what they mean.
>
> In the case above, the issue need not have arisen in the first place,
> because the induction variable in a range iterator isn't getting updated.
> It's getting rebound. In the view of a purer language design, each of those
> captures is capturing a different value. So yes, Python gets this wrong,
> because /i/ is going out of scope at the close of each loop. From the BitC
> perspective, by-reference capture should be producing a static type error
> because the captured reference exceeds the lifespan of its initialization.
>
> That isn't true an updating iteration. Given
>
> for (i = 1; i < 10; i++)
> closures.append(lambda x:x+i)
>
>
> The /i/ is not going out of scope, so the distinction between by-value and
> by-reference capture becomes significant. Further, the region may not
> expire, so we may not get a static type error.
>
> It is possible to resolve this in BitC by writing something like:
>
> for (i = 1; i < 10; i++)
> let i = i in
> closures.append(lambda x:x+i)
>
>
> but I think this places the burden on the wrong side of the discussion. I
> think what I'm coming down to here is that capture of mutables should be
> done by (shallow copied) value.
I think I agree. It's not an entirely consistent solution, since the
same kind of error can still occur, but it does resolve the
significant majority of the problem.
On the other hand, it does create the problem of what to do if you
actually *do* want to capture a mutable variable. E.g.,
let xs : List Int = list(...)
let sum: mutable Int = 0
iter (i -> sum += i) xs
As long as the closure doesn't escape the function stack, it would be
nice to preserve this idiom without any boxing.
>> The *other* possibility is to say that the variable introduced by the
>> loop is fresh, as in the following java:
>>
>> for ( final int i : someIterable ) {
>> closures.append(x -> x + i);
>> }
>>
>> If you don't require variable declarations, this seems a bit tricky...
>
>
> Not tricky at all. The clear resolution is that range-based /for/ is defined
> to introduce a fresh variable.
>
>>
>> because does the loop body also introduce a fresh scope? Java says
>> yes, but this is awkward in practice.
>
>
> In functional languages, the answer to this has always been "yes".
Even imperative languages (Python, Java, etc.) are pretty consistent
on this front.
> It seems to me that the default should be by-value capture. The more
> interesting question is whether it's worth the bother to introduce an
> overriding construct of some form. I see two ways to do this:
>
> let captured mutable x = initializer in ...
>
> or some annotation at the lambda itself indicating the type of capture it
> means to be doing. Hard to say which is cleaner, though in either case I
> think the iterator pattern should be using a fresh variable. BitC is trying
> to favor non-imperative constructs where possible. One advantage to the "let
> captured" construct is that it tells you explicitly that the mutable value
> isn't shallow.
I think this is solving my sum question from above, though I'm not
sure what the resulting solution would look like.
Geoffrey
_______________________________________________
bitc-dev mailing list
[email protected]
http://www.coyotos.org/mailman/listinfo/bitc-dev