import (
"fmt"
)
func main() {
for i := 0; i < 10; i++ {
defer func() {
fmt.Printf("func(): %v\n", i)
}()
defer printi(i)
defer fmt.Printf("i: %v\t", i)
}
}
func printi(i int) {
fmt.Printf("printi(): %v\t", i)
}
OUTPUT
======
i: 9 printi(): 9 func(): 10
i: 8 printi(): 8 func(): 10
i: 7 printi(): 7 func(): 10
i: 6 printi(): 6 func(): 10
i: 5 printi(): 5 func(): 10
i: 4 printi(): 4 func(): 10
i: 3 printi(): 3 func(): 10
i: 2 printi(): 2 func(): 10
i: 1 printi(): 1 func(): 10
i: 0 printi(): 0 func(): 10
It makes sense, scope-wise, but I'm not sure what to call it.
import (
"fmt"
)
func main() {
for i := 0; i < 10; i++ {
defer func(i int) {
fmt.Printf("func(): %v\n", i)
}(i)defer printi(i)
defer fmt.Printf("i: %v\t", i)
}
}
func printi(i int) {
fmt.Printf("printi(): %v\t", i)
}
OUTPUT
======
i: 9 printi(): 9 func(): 9
i: 8 printi(): 8 func(): 8
i: 7 printi(): 7 func(): 7
i: 6 printi(): 6 func(): 6
i: 5 printi(): 5 func(): 5
i: 4 printi(): 4 func(): 4
i: 3 printi(): 3 func(): 3
i: 2 printi(): 2 func(): 2
i: 1 printi(): 1 func(): 1
i: 0 printi(): 0 func(): 0
import (
"fmt"
)
func main() {
for i := 0; i < 10; i++ {
defer func() {
fmt.Printf("func(): %v\n", i)
}()
defer func(i int) {
fmt.Printf("func(i): %v\t", i)
}(i)defer printi(i)
defer fmt.Printf("i: %v\t", i)
}
}
func printi(i int) {
fmt.Printf("printi(i): %v\t", i)
}
OUTPUT
======
i: 9 printi(i): 9 func(i): 9 func(): 10
i: 8 printi(i): 8 func(i): 8 func(): 10
i: 7 printi(i): 7 func(i): 7 func(): 10
i: 6 printi(i): 6 func(i): 6 func(): 10
i: 5 printi(i): 5 func(i): 5 func(): 10
i: 4 printi(i): 4 func(i): 4 func(): 10
i: 3 printi(i): 3 func(i): 3 func(): 10
i: 2 printi(i): 2 func(i): 2 func(): 10
i: 1 printi(i): 1 func(i): 1 func(): 10
i: 0 printi(i): 0 func(i): 0 func(): 10
I don't understand. It does what it should do per specs. The behavior
is thus "wanted" and I fail to see what's surprising about it.
-j
Also: Have you checked the earlier example with the closure equivalent?
And forget about the scoping, it just confuses you even though it has no importance here.
On Oct 17, 2012 5:40 PM, "Dumitru Ungureanu" <itmi...@gmail.com> wrote:
> My main argument about "bug" behaviour would be that since i is locally scoped to the for loop, it shouldn't be available by way of defer with values that are not satisfying the parent code block conditions, as much as it's not available to printi() without being passed as a global variable or a parameter. You know, printi(i) versus printi(), something like that...
you need to understand all these points:
1. the closure you defer capture a reference to i (not its value)
2. because of 1, when the deferred func. finally executes, it will show the value of i at that time.
3. when the loop ends, i should be 10.
4. when the defered func. executes, the loop has already ended, so i is 10.
This is explained in the language spec:[...]
Whatever we call this kind of variable, it does not matter.Any way, it's accessible outside the function where it's declared or defined.
No offense, but I really think you made very little things clear in this
thread (and I really think you are not understanding the part of the
spec I quoted).
Cheers,
M;
Now consider wrapping the inner println inside a closure:
1: var f func()
2: for i := 0; i < 10; i++ {
3: f = func() { println(i) }
4: }
5: f()
This works, and prints "10". Why? Because scope tunnels through a
closure like a portal: everything that was visible to the code when
the closure is created is visible when the closure is run. In that
sense, the scope of i is expanded beyond the loop and into the
universe that the closure creates, so when it is executed in line 5 it
may see the variable i that would otherwise only exist for lines 2-4.
Thus, in a sense, i is accessible outside the loop. You could have
that closure return i's value instead of printing it and have the same
effect.
Does that clarify the difficulty you are having?
Dave.
func main() {
var i int
var f func()
For:
if i < 10 {
f = func() { println(i) }
i++
goto For
}
f()
}
... and my main argument against "bug" behavior is that i does for the for loop what is expected, there are exactly as many loops as they should: 10.
What I find interesting is the availability of a value for the local var i that normally shouldn't occur in code. It's not what can we do about it, it's what do we call it?
You're confusing scope and extent. Scope specifies the region of program text that can refer to the binding. Extent refers to the length of time that the binding exists.
The scope is limited, but the extent is not. This is typical for languages with first class functions.
Thomas
--
What do we call it? It's definitely not a bug... How about a "faq"?
http://golang.org/doc/go_faq.html#closures_and_goroutines
Although the faq examples use "go" rather than "defer", it is essentially the same: closures created in a loop that reference loop variables and are executed (potentially) after the loop has terminated, thus observing the terminal value of the loop variable. (Perhaps the faq wording could be updated; in its current form it can be interpreted as suggesting that this behavior is dependent on the closures being executed in separate goroutines.)
Peter
You're confusing scope and extent. Scope specifies the region of program text that can refer to the binding. Extent refers to the length of time that the binding exists.
The scope is limited, but the extent is not. This is typical for languages with first class functions.
Thomas
This actually makes more sense. But I'm thinking the GC should kick in sooner for my initial for loop and not make i available for the 10 value... if I may be so bold, and probably wrong.
It is inside the for loop textually, but not temporally.
--
GC has nothing to do with it.
--
On Oct 18, 2012 12:00 AM, "Dumitru Ungureanu" <itmi...@gmail.com> wrote:
> OK, in parameter-less lambda functions, the referencing environment (i) is not passed accordingly for each call. So each defer for that parameter-less lambda function in my code shares the same final instance i = 10. That still doesn't explain why the value 10 for i would be available inside a for loop with a i<10 condition.
so you think each loop iteration somehow creates new
instance for variable i?
it is not the case. i refer to the same variable, and its value
changes. the closure references that single instance,
so when the deferred function finally runs it display
the value of i at that time, which is 10 (loop ends when
i reaches 10).
so you think each loop iteration somehow creates new
instance for variable i?
it is not the case. i refer to the same variable, and its value
changes. the closure references that single instance,
so when the deferred function finally runs it display
the value of i at that time, which is 10 (loop ends when
i reaches 10).
--
It is not 10 inside the loop body / if statemement body. Several
people have told you, that not 'i', but a pointer to 'i' is captured.
So the value 10 is read by dereferencing that captured pointer to 'i'
in the moment the code in the closure executes and accesses 'i'. In
that time we are already outside the for loop / if statement exactly
b/c 'i' is now not anymore < 10.
Simple. Whenever you see a function header ('func()' in this case) in
an expression and it is followed by a body ("{ printlin i }" in this
case) then you're looking at an anonymous function. Anonymous
functions which capture variables outside theirs own body are
closures. 'f' is a variable of type 'func()', its value is the
anomymous function 'func() { ...' and the closure captures 'i'.
-j
The GC cannot collect 'i' because a reference (pointer) to it exists
in the closure. Why you are endlessly ignoring the
closures-capture-by-reference-in-Go fact? It was stated at least a
dozen times already in this thread.
-j
This help?
http://play.golang.org/p/6CC_r-X8Pn
My example using the if statement and the label FOR to simulate the for loop was suppose to get this discussion out of the way.
Yes, the for's loop i++ statement, even though it appears before the {...} code block, it will actually be part of it, as the last statement executed in that block, except for the first iteration.
The condition i < 10 will be the first evaluated statement in the block, on each iteration.
The interesting fact is the residual value 10 for 'i', from the previous step, being available on the last non-executing step, despite the condition i < 10 as being the first one evaluated which should make the whole i = 10 thing stop to a screeching halt, no matter what it did before, closures, lambdas, iterations.
--
On Wed, Oct 17, 2012 at 2:30 PM, Dumitru Ungureanu <itmi...@gmail.com> wrote:My example using the if statement and the label FOR to simulate the for loop was suppose to get this discussion out of the way.
Yes, the for's loop i++ statement, even though it appears before the {...} code block, it will actually be part of it, as the last statement executed in that block, except for the first iteration.
i++ will be evaluated after each iteration of the loop, unless you use a break or return during that iteration.
The condition i < 10 will be the first evaluated statement in the block, on each iteration.i < 10 is not a statement. It is an expression. It evaluates to true or false, the same way 4 + 5 evaluates to 9. If it is true at the beginning of the iteration, the iteration proceeds. If it is false at the beginning of the iteration, the loop terminates.
The interesting fact is the residual value 10 for 'i', from the previous step, being available on the last non-executing step, despite the condition i < 10 as being the first one evaluated which should make the whole i = 10 thing stop to a screeching halt, no matter what it did before, closures, lambdas, iterations.Since the condition is only evaluated at the beginning of each iteration, it doesn't stop any change to the value of i "to a screeching halt". It is not a constraint on the value of i throughout an iteration of the loop. It is merely a condition checked at the beginning of each iteration.
That's true, but it's a little misleading, because declaring the variable inside the "for" gives it only scope for the for loop, while declaring it before on a separate line gives it scope for the rest of the function.
i < 10 is not a statement. It is an expression. It evaluates to true or false, the same way 4 + 5 evaluates to 9. If it is true at the beginning of the iteration, the iteration proceeds. If it is false at the beginning of the iteration, the loop terminates.
Normally. That's why is so interesting what's happening with i == 10.
Since the condition is only evaluated at the beginning of each iteration, it doesn't stop any change to the value of i "to a screeching halt". It is not a constraint on the value of i throughout an iteration of the loop. It is merely a condition checked at the beginning of each iteration.
No, it doesn't stop the i++. But it should stop the i==10 use.
... i++ will be executed on each iteration, *as part of the iteration*, at the end of it.
That's part of the point. Scope does not exist at run time. It is only relevant to source code.The other part of the point is that if you give i a scope that covers the whole function, you can use it after the loop has terminated—and its value will be 10. The fact that i is 10 after the loop terminates has nothing to do with scopes or closures. It's just how C-style for loops work.
A distinction without a difference. It doesn't matter whether you consider i++ to be part of the iteration, or something that happens (immediately) after each iteration. You're changing the definition of the word "iteration", not what actually happens.
It does. You'd have two things at the beginning of each iteration: the increment and the condition. A clash with unpredictable results.
When the for loop is deconstructed, the increment becomes part of the iteration code block, as the last statement.
So, I could be very well have all my variables declared as global? :)
Of course, in most cases a variable's scope has an effect on its extent (when it is created and when it is destroyed). If a variable is local to a certain block, a new memory location is allocated for the variable each time that block is entered. But if the block is only executed once, the variable might as well be global.
There is nothing that would "stop the i == 10 use".
Chris
--
Chris "allusive" Dollin
OUTPUT
======
i: 9 printi(): 9 func(): 10
i: 8 printi(): 8 func(): 10
i: 7 printi(): 7 func(): 10
i: 6 printi(): 6 func(): 10
i: 5 printi(): 5 func(): 10
i: 4 printi(): 4 func(): 10
i: 3 printi(): 3 func(): 10
i: 2 printi(): 2 func(): 10
i: 1 printi(): 1 func(): 10
i: 0 printi(): 0 func(): 10
OUTPUT
======
i: 9 printi(): 9 func(): 9
i: 8 printi(): 8 func(): 9
i: 7 printi(): 7 func(): 9
i: 6 printi(): 6 func(): 9
i: 5 printi(): 5 func(): 9
i: 4 printi(): 4 func(): 9
i: 3 printi(): 3 func(): 9
i: 2 printi(): 2 func(): 9
i: 1 printi(): 1 func(): 9
i: 0 printi(): 0 func(): 9
The point is that the Go spec does not guarantee that a variable will be annihilated the instant the program leaves its scope. In fact, it guarantees just the opposite: that the variable will be around as long as there is a reference to it. Since, in both programs I posted, there was a reference to i until the function returned, the extent of i was the same in each, even though the scope wasn't.
since 9 is the last *valid* value for the for loop. I know, I know, validation post incrementation, closure means reference..., but they work from "inside" the for loop with "invalid" for loop values. Interesting, isn't it?
No, they don't work from the "inside" of the loop. Your closure is
executed *after* your loop has terminated, when 'i' deterministically
has a value of 10 and the reference in the closure just derefences to
that very value.
-j
So what should this code do? panic?var i intfor i = 0; i < 10; i++ {}fmt.Println("i =", i)
How is this interesting? It's the natural effect of a by-reference closure that it will observe the value as it is at the time it is executed and not as it was at some point in the scope in which the closure was created.
Would you say that passing a pointer to a loop control variable and
dereferencing that pointer *after* the loop terminates `can transmit
"illegal" values for that for loop to "outside" actors.`???
Because if not, than you're contradicting yourself.
-j
func main() {
var x int
var i *int
i = &x
Go to here: http://writecodeonline.com/javascript/
Paste there this:
var f;
for (i = 0; i<10;i++) {
f = function() {alert(i);};
}
f();
Click "[run code]" ;-)
-j