Scope: for + defer + lambda function = interesting result

1,545 views
Skip to first unread message

Dumitru Ungureanu

unread,
Oct 17, 2012, 4:59:02 AM10/17/12
to golan...@googlegroups.com
Interesting scope and evaluation: lambda func() takes a value outside the scope of the for true condition: 10.

package main
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.

Miguel Pignatelli

unread,
Oct 17, 2012, 5:09:30 AM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com

On 17/10/12 09:59, Dumitru Ungureanu wrote:
> Interesting scope and evaluation: lambda func() takes a value outside
> the scope of the for true condition: 10.
>

The key point is that the arguments to the deferred functions are
evaluated and copied in the invocation of the defer. So:

> defer func() {
> fmt.Printf("func(): %v\n", i)
> }()

No arguments to func(), so i is not copied and the final value of i is
used when the deferred function is executed.

> defer printi(i)
> defer fmt.Printf("i: %v\t", i)

In both cases, i is the argument of the deferred function, so its value
is copied and used when the deferred function is evaluated.

M;


> 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.
>
> --
>
>

stevewang

unread,
Oct 17, 2012, 5:11:02 AM10/17/12
to golan...@googlegroups.com
When those goroutines are scheduled to execute, the value of i outside them is already changed to 10 by main. 
So the result is not surprising.

Jan Mercl

unread,
Oct 17, 2012, 5:11:00 AM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
On Wed, Oct 17, 2012 at 10:59 AM, Dumitru Ungureanu <itmi...@gmail.com> wrote:

Go closures simply capture variables visible in the current scope, but
by reference instead of by value: http://play.golang.org/p/nSf0vrU-U-

The above example is effectively equal to: http://play.golang.org/p/GWyxxTIDCD

-j

Dumitru Ungureanu

unread,
Oct 17, 2012, 5:13:52 AM10/17/12
to golan...@googlegroups.com
I know about this:


package main
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

Still...

Dumitru Ungureanu

unread,
Oct 17, 2012, 5:21:09 AM10/17/12
to golan...@googlegroups.com
I said it makes sense, but it's an interesting result. Some may even call it a "bug" as in "unwanted behaviour".

Here's an improved example. By contrast, can a print() be executed 10 times with the i = 10 value in the for loop?


package main
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

Jan Mercl

unread,
Oct 17, 2012, 5:24:01 AM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
On Wed, Oct 17, 2012 at 11:21 AM, Dumitru Ungureanu <itmi...@gmail.com> wrote:
> I said it makes sense, but it's an interesting result. Some may even call it
> a "bug" as in "unwanted behaviour".

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

Dumitru Ungureanu

unread,
Oct 17, 2012, 5:29:07 AM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
On Wednesday, October 17, 2012 12:24:30 PM UTC+3, Jan Mercl wrote:
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

Well, you have to admit it executes code with a value that contradicts the boolean condition for the for loop, even if it's in tune with the scope and defer parts.

Dumitru Ungureanu

unread,
Oct 17, 2012, 5:30:31 AM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
... and i is local scoped to the for loop.

Jan Mercl

unread,
Oct 17, 2012, 5:31:50 AM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
On Wed, Oct 17, 2012 at 11:29 AM, Dumitru Ungureanu <itmi...@gmail.com> wrote:
> Well, you have to admit it executes code with a value that contradicts the
> boolean condition for the for loop, even if it's in tune with the scope and
> defer parts.

It doesn't. And forget about the scoping, it just confuses you even
though it has no importance here.

Also: Have you checked the earlier example with the closure equivalent?

-j

Dumitru Ungureanu

unread,
Oct 17, 2012, 5:35:10 AM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
Also: Have you checked the earlier example with the closure equivalent?
 
Didn't had a chance yet, I'm at work and there's not possible for me to get to play.golang.org. I'll check it out later, when I get home.



And forget about the scoping, it just confuses you even though it has no importance here.

I'm listening.

Jan Mercl

unread,
Oct 17, 2012, 5:35:02 AM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
On Wed, Oct 17, 2012 at 11:30 AM, Dumitru Ungureanu <itmi...@gmail.com> wrote:
> ... and i is local scoped to the for loop.

And is captured in the closure by reference. Thus it must and does
exist outside its scope. It's like:
http://play.golang.org/p/QJQ08vjfOA

-j

Dumitru Ungureanu

unread,
Oct 17, 2012, 5:40:41 AM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
I can wait to get home to check the examples. :)

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...

Dumitru Ungureanu

unread,
Oct 17, 2012, 5:53:38 AM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
... 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?

stevewang

unread,
Oct 17, 2012, 5:55:39 AM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
A local variable is acctually accessible to the anonymous function created in the same code scope.
As I know, in this case, the so-called "local" variable will be allocated in heap space.

minux

unread,
Oct 17, 2012, 5:58:48 AM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com


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.

chris dollin

unread,
Oct 17, 2012, 6:01:40 AM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
On 17 October 2012 10:53, Dumitru Ungureanu <itmi...@gmail.com> wrote:
> ... 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.

What?

The variable i will take on values 0-10. When it reaches 10
(by execution of the i++) then the loop will stop, but i will be
10.

The closures have captured references to the variable i
(there is only one per execution of this function) and so when
they run (when the defers unwind) they will print its value.

> It's not what can we do about it, it's what do we call it?

Do we need to call it anything?

Chris

--
Chris "allusive" Dollin

Jesse McNelis

unread,
Oct 17, 2012, 6:02:13 AM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
On Wed, Oct 17, 2012 at 8:53 PM, Dumitru Ungureanu <itmi...@gmail.com> wrote:
> 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?

It's called a closure.
In this case it's a closure that be being deferred.


--
=====================
http://jessta.id.au

chris dollin

unread,
Oct 17, 2012, 6:03:47 AM10/17/12
to stevewang, golan...@googlegroups.com, Dumitru Ungureanu
On 17 October 2012 10:55, stevewang <steve....@gmail.com> wrote:
> A local variable is acctually accessible to the anonymous function created
> in the same code scope.
> As I know, in this case, the so-called "local" variable will be allocated in
> heap space.

I don't see why it's a 'so-called "local" variable'. It really is a
local variable.
Allocating it on the heap doesn't make it non-local.

Dumitru Ungureanu

unread,
Oct 17, 2012, 6:07:03 AM10/17/12
to golan...@googlegroups.com
Like I said, scope-wise, defer-wise, closure-wise, it all makes sense. I'm not "disputing" anything but the availability of a value.

I'm only finding interesting that you can actually "go against" the condition in the for loop and be able to use for code inside the for loop, automatically generated values for i (i++), that normally you couldn't. By contrast, printi() won't be able to use the automatically generated 10 value for i..I guess?

Do we need to call it again? I guess...not?

Miguel Pignatelli

unread,
Oct 17, 2012, 6:11:19 AM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
On 17/10/12 10:21, Dumitru Ungureanu wrote:
> I said it makes sense, but it's an interesting result. Some may even
> call it a "bug" as in "unwanted behaviour".
>

This is explained in the language spec:

http://golang.org/ref/spec#Defer_statements
"Each time the "defer" statement executes, the function value and
parameters to the call are evaluated as usual and saved anew but the
actual function is not invoked"

This means that:
> defer func() {
> fmt.Printf("func(): %v\n", i)
> }()

There is no parameters to the closure, so i is not evaluated nor saved

> defer fmt.Printf("i: %v\t", i)
i is a parameter to the deferred function, so it is evaluated and saved
anew.

M;
> --
>
>

stevewang

unread,
Oct 17, 2012, 6:15:41 AM10/17/12
to golan...@googlegroups.com, stevewang, Dumitru Ungureanu, ehog....@googlemail.com
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.

Dumitru Ungureanu

unread,
Oct 17, 2012, 6:18:54 AM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
On Wednesday, October 17, 2012 1:11:24 PM UTC+3, emepyc wrote:
This is explained in the language spec:[...]

No offense, but no news here... I thought I made this clear.

Dumitru Ungureanu

unread,
Oct 17, 2012, 6:21:12 AM10/17/12
to golan...@googlegroups.com, stevewang, Dumitru Ungureanu, ehog....@googlemail.com
On Wednesday, October 17, 2012 1:15:41 PM UTC+3, stevewang wrote:
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.

It's not accessible outside, it's still local, the interesting part is that you can use a value for it you shouldn't be able too. There's a difference.

Miguel Pignatelli

unread,
Oct 17, 2012, 6:26:13 AM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com, stevewang, ehog....@googlemail.com
It is called closure and it is nothing specific to Go:

http://en.wikipedia.org/wiki/Closure_(computer_science)

M;

> --
>
>

stevewang

unread,
Oct 17, 2012, 6:26:56 AM10/17/12
to golan...@googlegroups.com, stevewang, Dumitru Ungureanu, ehog....@googlemail.com
Maybe we have different defines for "accessible" or "local".
In my view, this a case that local variable "i" is accessible outside f():
var p *int
func f() {
var i int = 100
p = &i
}
func main() {
f()
fmt.Println(*p)
}

//output:
100

Jan Mercl

unread,
Oct 17, 2012, 6:27:30 AM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com, stevewang, ehog....@googlemail.com
On Wed, Oct 17, 2012 at 12:21 PM, Dumitru Ungureanu <itmi...@gmail.com> wrote:
> It's not accessible outside, it's still local, the interesting part is that
> you can use a value for it you shouldn't be able too. There's a difference.

No, you *should be able* to access a variable you have a reference to
from anywhere.

-j

chris dollin

unread,
Oct 17, 2012, 6:32:28 AM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com, stevewang
Exactly the same kind of use can be made by making a pointer to i
and accessing it after the loop has finished.

I don't see where the justification of "you shouldn't be able to" comes
from.

Dumitru Ungureanu

unread,
Oct 17, 2012, 6:33:37 AM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu, stevewang, ehog....@googlemail.com
Agreed. In my case, i, for the lambda function, is not accessible from outside the for loop, it's local to that.

chris dollin

unread,
Oct 17, 2012, 6:36:21 AM10/17/12
to stevewang, golan...@googlegroups.com, Dumitru Ungureanu
On 17 October 2012 11:26, stevewang <steve....@gmail.com> wrote:
> Maybe we have different defines for "accessible" or "local".
> In my view, this a case that local variable "i" is accessible outside f():

Not by name.

Function-locality (actually, block-locality) means two things: that the name
of the variable is only visible inside the scope of its declaration, and that
each execution of the declaration creates a new variable.

(And in some languages, eg C, that variable evaporates when the scope is left,
and accesses to "it" are forbidden; that is not true in Go).

Dumitru Ungureanu

unread,
Oct 17, 2012, 6:39:33 AM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu, stevewang, ehog....@googlemail.com
Well, I'm not making a pointer, do I? I'm making a defer for a lambda. And i get an interesting result for a local variable. Closure or no closure.

chris dollin

unread,
Oct 17, 2012, 6:44:51 AM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com, stevewang
On 17 October 2012 11:39, Dumitru Ungureanu <itmi...@gmail.com> wrote:
> Well, I'm not making a pointer, do I? I'm making a defer for a lambda.

The effect you're seeing -- access to a state where i = 10 -- can be
obtained by deferring a closure OR by taking a pointer, which is what
the closure is doing. It's not special magic to do with defer, and its not
specific to closures.

>> I
still

Miguel Pignatelli

unread,
Oct 17, 2012, 7:29:21 AM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
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;


> --
>
>

David Symonds

unread,
Oct 17, 2012, 8:17:43 AM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com, stevewang, ehog....@googlemail.com
On Wed, Oct 17, 2012 at 9:33 PM, Dumitru Ungureanu <itmi...@gmail.com> wrote:

> Agreed. In my case, i, for the lambda function, is not accessible from
> outside the for loop, it's local to that.

Perhaps you're getting confused by the influence that closures have on scope.

Consider this code:
1: for i := 0; i < 10; i++ {
2: println(i)
3: }
4: println(i)
Line 4 yields a compilation error because i is not in scope there. I
think you get that just fine: The scope of i is the loop.

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.

Dumitru Ungureanu

unread,
Oct 17, 2012, 9:43:43 AM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
On Wednesday, October 17, 2012 2:29:24 PM UTC+3, emepyc wrote:
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;

 I'm really sorry, this must be a personal failure of mine, not being able to make things clearer. I'll work on that. And on the specs. Thanks.

Dumitru Ungureanu

unread,
Oct 17, 2012, 10:00:29 AM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu, stevewang, ehog....@googlemail.com
On Wednesday, October 17, 2012 3:17:56 PM UTC+3, David Symonds wrote:
 
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.


I'm not sure. Interesting example. Is your code really a closure?

package main
func main() {
    var i int
    var f func()
For:
    if i < 10 {
        f = func() { println(i) }
        i++
        goto For
    }
    f()
}


chris dollin

unread,
Oct 17, 2012, 10:24:14 AM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com, stevewang
On 17 October 2012 15:00, Dumitru Ungureanu <itmi...@gmail.com> wrote:
> On Wednesday, October 17, 2012 3:17:56 PM UTC+3, David Symonds wrote:
>
>> 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.

> I'm not sure. Interesting example. Is your code really a closure?

Yes, his code really truly is a closure. (He's wrong about "the scope of i
is expanded beyond the loop", since scope refers to regions of text in
which the variable's name can be used to get to the variable, and the
name i cannot be used to get to the variable i after the loop. Scope isn't
to do with where you might /change/ the variable, since pointers allow
you to smuggle accesss to a variable to pretty much anywhere.)

> package main
>
> func main() {
> var i int
> var f func()
>
> For:
> if i < 10 {
> f = func() { println(i) }
> i++
> goto For
> }
> f()
> }

What about it?

Miguel Pignatelli

unread,
Oct 17, 2012, 10:26:30 AM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
On 17/10/12 15:00, Dumitru Ungureanu wrote:
> On Wednesday, October 17, 2012 3:17:56 PM UTC+3, David Symonds wrote:
>
> 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.
>
>
>
> I'm not sure. Interesting example. Is your code really a closure?

Yes, it is a closure because i is used out of its "original scope"
f is able to "close" over the variable and refer to it in a different
lexical scope.

>
> packagemain
>
> func main() {
> var i int
> var f func()
> For:
> if i < 10 {
> f = func() { println(i) }
> i++
> goto For
> }
> f()
> }
>

This is not a closure, because f() is called when i is still in its
lexical scope.

M;

>
>
> --
>
>

chris dollin

unread,
Oct 17, 2012, 10:35:08 AM10/17/12
to Miguel Pignatelli, Dumitru Ungureanu, golan...@googlegroups.com
On 17 October 2012 15:26, Miguel Pignatelli <eme...@gmail.com> wrote:
> On 17/10/12 15:00, Dumitru Ungureanu wrote:
>>
>> On Wednesday, October 17, 2012 3:17:56 PM UTC+3, David Symonds wrote:
>>
>> 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?
>>
>> I'm not sure. Interesting example. Is your code really a closure?
>
>
> Yes, it is a closure because i is used out of its "original scope"

i is used in its scope, which covers the function literal. I don't
know what you mean by "original scope".

> f is able to "close" over the variable and refer to it in a different
> lexical scope.

It's the same scope. Well, a subscope.

>> packagemain
>>

>> func main() {
>> var i int
>> var f func()
>> For:
>> if i < 10 {
>> f = func() { println(i) }
>> i++
>> goto For
>> }
>> f()
>> }
>
> This is not a closure, because f() is called when i is still in its lexical
> scope.

No, it is a closure. Being a closure isn't an accident of when you
happen to call the function, just of being a function literal [as Go calls
them] that refers to variables declared outside the literal [but inside a
function].

Peter S

unread,
Oct 17, 2012, 10:37:09 AM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
On Wed, Oct 17, 2012 at 6:53 PM, Dumitru Ungureanu <itmi...@gmail.com> wrote:
... 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?

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

Thomas Bushnell, BSG

unread,
Oct 17, 2012, 11:07:50 AM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com

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

--
 
 

Dumitru Ungureanu

unread,
Oct 17, 2012, 12:00:29 PM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
On Wednesday, October 17, 2012 5:37:23 PM UTC+3, speter wrote:
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

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.

BTW, my FOR label example is not a closure. It just simulates a for loop with an if and it clearly sets i as available before and after the FOR label simulated loop, while the i++ explains the 10 value for the function call.

And in this example, f = func() { println(i) } it's just an assignment. I'm not sure how assignment translates to closure... but I'm willing to learn.

Dumitru Ungureanu

unread,
Oct 17, 2012, 12:02:48 PM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
On Wednesday, October 17, 2012 6:08:02 PM UTC+3, Thomas Bushnell, BSG wrote:

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.

Andy Balholm

unread,
Oct 17, 2012, 12:09:31 PM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
On Wednesday, October 17, 2012 9:02:48 AM UTC-7, Dumitru Ungureanu wrote:
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.

The GC only collects values when there are no more references to them. The closure has a reference to i. Therefore i will not be collected until the closure has completed.

Dumitru Ungureanu

unread,
Oct 17, 2012, 12:10:50 PM10/17/12
to golan...@googlegroups.com
Which is exactly why it makes i = 10 available when it shouldn't...?

Thomas Bushnell, BSG

unread,
Oct 17, 2012, 12:17:04 PM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com

It is inside the for loop textually, but not temporally.

--
 
 

Thomas Bushnell, BSG

unread,
Oct 17, 2012, 12:17:49 PM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com

GC has nothing to do with it.

On Oct 17, 2012 9:00 AM, "Dumitru Ungureanu" <itmi...@gmail.com> wrote:
--
 
 

minux

unread,
Oct 17, 2012, 12:19:02 PM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com


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).

Dumitru Ungureanu

unread,
Oct 17, 2012, 12:19:30 PM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
The extend, yes.

Dumitru Ungureanu

unread,
Oct 17, 2012, 12:31:12 PM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
On Wednesday, October 17, 2012 7:19:09 PM UTC+3, minux wrote:

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).


I'm not really sure what you mean. i getting a new value is a new instance for i.

I suppose you mean something else by it, like database instances, or pointers, but I confess I'm lost.

Thomas Bushnell, BSG

unread,
Oct 17, 2012, 12:32:24 PM10/17/12
to Dumitru Ungureanu, golang-nuts
GC does not affect the extent of variables or objects. This is a common misconception.

In Go, just as in Scheme, pretty much all variables and objects have indefinite extent. The job of GC is to reclaim memory in cases where the system can _prove_ that, even though the object's extent continues, it is impossible for the program to ever refer to it again, so the storage for it can be reclaimed.

The scoping rules of Go are very much like Pascal (except that Go lets you have a new scope in more fine-grained ways than Pascal). Pascal works very hard to guarantee what you could call the static-scoping-limited-extent-equivalence rule (or the scope/extent equivalence rule for short):

"Every binding is attached to a region of program text, and all references to the binding can only occur within that region of program text. Further, these scopes correspond to activations which have determinate beginnings and endings in time, and no references to the binding can occur outside that limited extent."

It is in order to maintain the scope/extent equivalence rule that Pascal has procedure and function parameters, but no procedure or function variables, by the way. The rules for goto in Pascal are also designed to maintain the scope/equivalence rule.

However, in a language with first-class functions, you cannot sensibly maintain the rule, because functions defined in a scope can be called after the relevant activation has terminated. Either you prohibit the functions from referring to local variables in the scope ("prohibit closures"), or you coerce the values weirdly ("implement closures wrong"), or you do what Go and Scheme do: ("infinite extent but limited scope").

In your example, you are quite right that the "for" loop establishes a scope, and the scope it establishes is limited strictly to the text of the for loop. But you are incorrect that the extent should match the scope. The effect of having first-class functions is to make the scope/extent equivalence rule untenable.

The "for" loop establishes a binding, which is in scope for the textual segment of the for loop, but which lives forever. (Don't say "no, it lives only until the next GC".)

Thomas




On Wed, Oct 17, 2012 at 9:19 AM, Dumitru Ungureanu <itmi...@gmail.com> wrote:
The extend, yes.

--
 
 

Thomas Bushnell, BSG

unread,
Oct 17, 2012, 12:33:04 PM10/17/12
to Dumitru Ungureanu, golang-nuts
No. The variable gets a new value, but it's an assignment to the already existing variable, not a new variable.


--
 
 

Dumitru Ungureanu

unread,
Oct 17, 2012, 12:39:05 PM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
Yes, I misspoke, a new instance would also mean a new memory space, I was just saying that with i, that's not the case, outside pointers, there's really no way to create such an instance.

Dumitru Ungureanu

unread,
Oct 17, 2012, 12:44:26 PM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu

That makes sense, and it's a wonderful explanation, one I was guessing my self, but now, thanks to you, I have better words for it. Thank you.

But this covers the Go world. Would this be true otherwise? Starting with pseudocode. Any closure in any language implementing a for loop would potentially perform the same? That's what I was finding interesting, possibly, this being a Go trait.

Thomas Bushnell, BSG

unread,
Oct 17, 2012, 12:52:26 PM10/17/12
to Dumitru Ungureanu, golang-nuts
Every language can make its own decisions.

The way Go does it is basically the way that every language with block scoping and first-class functions does it. Usually they say things like "have closures" or "implement closures correctly", but really, "closure" is an implementation method, not a statement of semantics, dating to the history of Lisp when this stuff was still being worked out and folks were often quite unclear about it.

Scheme and Go and Common Lisp do exactly the same thing here, likewise Python and Ruby, if I'm not mistaken. Javascript, etc. Any language with "static scoping" (= textual scoping) and first-class functions should be assumed to work this way as a general rule. 

Dumitru Ungureanu

unread,
Oct 17, 2012, 12:56:42 PM10/17/12
to golan...@googlegroups.com
Closers, pointers, I get it.

Just to be perfectly clear, I'm talking about that having a i < 10 condition yet being able to use the i = 10 residual value in a for loop with a lambda function closure or otherwise.


Jan Mercl

unread,
Oct 17, 2012, 12:59:05 PM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
On Wed, Oct 17, 2012 at 6:00 PM, 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.

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.

Please stop even thinking in term of lambdas, scopes and other crap.
Well, it's not crap, but you have to first grab the first principles,
then you can call the concept any fancy name you can find for it.

Closures captures variables either by value or by reference (a pointer
to the variable, its address in memory wherever it is). Go closures
capture by reference (pointers), some other language may choose to
capture by value.

Now, in the Go case, just think about the expansion of the closure as
a stand alone function with pointer parameters for all of its captured
variables. It is just like that and it is that simple. Earlier was
discussed even a concrete example for this.

> BTW, my FOR label example is not a closure. It just simulates a for loop
> with an if and it clearly sets i as available before and after the FOR label
> simulated loop, while the i++ explains the 10 value for the function call.
>
> And in this example, f = func() { println(i) } it's just an assignment. I'm
> not sure how assignment translates to closure... but I'm willing to learn.

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

Jan Mercl

unread,
Oct 17, 2012, 1:01:41 PM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
On Wed, Oct 17, 2012 at 6:02 PM, Dumitru Ungureanu <itmi...@gmail.com> wrote:
> 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.

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

Dumitru Ungureanu

unread,
Oct 17, 2012, 1:09:37 PM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
On Wednesday, October 17, 2012 7:59:37 PM UTC+3, Jan Mercl wrote:
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.

I already said I'm not disputing the closure aspect of this. And I imagine a copy of 'i''s value at that moment is captured: a reference environment is created, as in new memory.

Interesting part is, that since we are outside the for loop, 'i' is now not anymore period. 'i' is a local variable to for.
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

My FOR label example I'm pretty sure is not a closure. But I may be wrong. :)

Dumitru Ungureanu

unread,
Oct 17, 2012, 1:12:57 PM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
On Wednesday, October 17, 2012 8:02:14 PM UTC+3, Jan Mercl wrote:
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

Sorry if it seems annoying. I imagine it does.

I'm saying this again: closer-wise is fine. It's OK. It's perfect. It the dot on the i.
Value-wise is ... interesting for the i.

Steven Blenkinsop

unread,
Oct 17, 2012, 1:27:55 PM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
You're thinking of the condition i < 10 as a constraint on the values i can take. It is not. It is a condition that is evaluated before each iteration of the for loop. If it fails, the loop terminates. This means that i has to have a value not conforming to the condition after the loop terminates normally (no break, return, or panic). Otherwise, the loop wouldn't have terminated. To put it another way:

for i := 0; i < 10; {
    i = 10
    defer func(){ println(i) }()
}

You know i is 10 because you assigned it that value, even though it violates the condition. The i++ accomplishes the same thing at the end of an iteration where i == 9.

Dumitru Ungureanu

unread,
Oct 17, 2012, 1:29:12 PM10/17/12
to golan...@googlegroups.com
To conclude, a lot of you in this thread tried to explain to me the basics of closures, pointers and stuff. While I'm grateful for any insight, and while may be seeing more clearly a few things now, it's not the thing I was looking for.

Closer-wise, the fact is, if not passed by value func() {...}(), 'i' it will evaluate to a value of 10, passe by reference. Otherwise, func(i int) {...}(i), 'i' it will evaluate to proper values: 0...9. It's perfectly normal.

Loop-wise, everything works as intended: there are 10 iterations, consistent with the i < 10 condition. Perfectly normal also.

But the fact that a residual value of 10 (due to the last i++ inside for's last loop, before the i < 10 condition), a residual value which exists for such a short span yet it can be used, that is what I find interesting.

Jan Mercl

unread,
Oct 17, 2012, 1:48:57 PM10/17/12
to Steven Blenkinsop, Dumitru Ungureanu, golan...@googlegroups.com
On Wed, Oct 17, 2012 at 7:27 PM, Steven Blenkinsop <stev...@gmail.com> wrote:
> You're thinking of the condition i < 10 as a constraint on the values i can
> take. It is not. It is a condition that is evaluated before each iteration
> of the for loop. If it fails, the loop terminates. This means that i has to
> have a value not conforming to the condition after the loop terminates
> normally (no break, return, or panic). Otherwise, the loop wouldn't have
> terminated. To put it another way:
>
> for i := 0; i < 10; {
> i = 10
> defer func(){ println(i) }()
> }
>
> You know i is 10 because you assigned it that value, even though it violates
> the condition.

There's no violation of any condition. This thread is gradually
becoming a gem of its class.

-j

Steven Blenkinsop

unread,
Oct 17, 2012, 2:11:38 PM10/17/12
to Jan Mercl, Dumitru Ungureanu, golan...@googlegroups.com
These statements are both true:

1) There is no condition (constraint) violated by assigning 10 to i.
2) There exists a condition which the value 10 doesn't satisfy (and which eventually gets evaluated on the value of i).

You rebutted my statement of (2) by stating (1), even though they are not contradictory, and even though I'd already stated (1) in that very same posting.

Dumitru Ungureanu

unread,
Oct 17, 2012, 2:30:11 PM10/17/12
to golan...@googlegroups.com
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.

Andy Balholm

unread,
Oct 17, 2012, 2:33:58 PM10/17/12
to golan...@googlegroups.com
The following two programs will compile to exactly the same machine code:

package main

import (
"fmt"
)

func main() {
var i int
for i = 0; i < 10; i++ {
defer func() {
fmt.Printf("func(): %v\n", i)
}()
}
}

-------------------------------
and

package main

import (
"fmt"
)

func main() {
for i := 0; i < 10; i++ {
defer func() {
fmt.Printf("func(): %v\n", i)
}()
}
}
 

bryanturley

unread,
Oct 17, 2012, 2:38:28 PM10/17/12
to golan...@googlegroups.com

Dumitru Ungureanu

unread,
Oct 17, 2012, 2:45:55 PM10/17/12
to golan...@googlegroups.com
This be more useful?
http://play.golang.org/p/-FwyM067eU

On Wednesday, October 17, 2012 9:38:28 PM UTC+3, bryanturley wrote:
This help?
http://play.golang.org/p/6CC_r-X8Pn


Dumitru Ungureanu

unread,
Oct 17, 2012, 2:48:17 PM10/17/12
to golan...@googlegroups.com
Not really, it proves all that's been said: unless transmitted by value, close means reference.

Steven Blenkinsop

unread,
Oct 17, 2012, 2:54:26 PM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
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. 

Thomas Bushnell, BSG

unread,
Oct 17, 2012, 2:55:26 PM10/17/12
to Andy Balholm, golang-nuts
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.



--
 
 

Dumitru Ungureanu

unread,
Oct 17, 2012, 3:01:03 PM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
On Wednesday, October 17, 2012 9:55:39 PM UTC+3, Steven Blenkinsop wrote:
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.


i++ will be executed on each iteration, at the end of it.

 
 
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.


Normally. That's why is so interesting what's happening with i == 10.
 

 

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. 



No, it doesn't stop the i++. But it should stop the i==10 use.

Dumitru Ungureanu

unread,
Oct 17, 2012, 3:05:25 PM10/17/12
to golan...@googlegroups.com
... i++ will be executed on each iteration, *as part of the iteration*, at the end of it.

Andy Balholm

unread,
Oct 17, 2012, 3:07:45 PM10/17/12
to golan...@googlegroups.com, Andy Balholm
On Wednesday, October 17, 2012 11:55:39 AM UTC-7, Thomas Bushnell, BSG wrote:
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.

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.

chris dollin

unread,
Oct 17, 2012, 3:09:07 PM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
On 17 October 2012 20:01, Dumitru Ungureanu <itmi...@gmail.com> wrote:

>
> No, it doesn't stop the i++. But it should stop the i==10 use.

What makes you think so?

Certainly I can't think of anything in the Go spec that
would suggest it.

When i is 9, the loop does another execution. Then i
is incremented -- the i++ says so. Since i was 9, it is now
10. i still exists: there's no reason for it not to. Any existing
references (such as those in a closure) will be referring
to a variable with the value 10. Then the loop condition is
checked, and fails, /because i is [still] 10/, and the loop
terminates /and i is 10/. The defers unwind /and i is still 10/
and the closure runs and accesses the variable i /which is
still 10/.

There is nothing that would "stop the i == 10 use".

Chris

--
Chris "allusive" Dollin

Steven Blenkinsop

unread,
Oct 17, 2012, 3:09:07 PM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
On Wed, Oct 17, 2012 at 3:01 PM, Dumitru Ungureanu <itmi...@gmail.com> wrote:
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.
 

Always. The loop never proceeds if i == 10 at the beginning of an iteration. That is not contradicted by any example posted in this thread.


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. 

It stops an iteration where i == 10 at the beginning of that iteration, and nothing else. 

... i++ will be executed on each iteration, *as part of the iteration*, at the end of it.

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.

Dumitru Ungureanu

unread,
Oct 17, 2012, 3:17:16 PM10/17/12
to golan...@googlegroups.com, Andy Balholm
On Wednesday, October 17, 2012 10:07:45 PM UTC+3, Andy Balholm wrote:
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.

So, I could be very well have all my variables declared as global? :)

Dumitru Ungureanu

unread,
Oct 17, 2012, 3:20:19 PM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
On Wednesday, October 17, 2012 10:09:35 PM UTC+3, Steven Blenkinsop wrote
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.

Steven Blenkinsop

unread,
Oct 17, 2012, 3:26:55 PM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
On Wed, Oct 17, 2012 at 3:20 PM, Dumitru Ungureanu <itmi...@gmail.com> wrote:

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.
 
I was using the word "iteration" loosely to mean "execution of the loop body". I sometimes also used it to mean "evaluation of the condition -> execution of the loop body -> execution of the post statement". Sorry for the inconsistency, but it isn't normally, and should never be, a problem, since my meaning should be clear either way.

Andy Balholm

unread,
Oct 17, 2012, 3:28:04 PM10/17/12
to golan...@googlegroups.com, Andy Balholm
On Wednesday, October 17, 2012 12:17:16 PM UTC-7, Dumitru Ungureanu wrote:
So, I could be very well have all my variables declared as global? :)

It wouldn't make any difference in this program.

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.

Dumitru Ungureanu

unread,
Oct 17, 2012, 3:35:47 PM10/17/12
to golan...@googlegroups.com, Andy Balholm
On Wednesday, October 17, 2012 10:28:04 PM UTC+3, Andy Balholm wrote:
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.

Hmmm... I thought there's no difference in the machine code, whether the variable is declared as global or local, so how can either one have effect on the extent? Just like that? If you mean the GC, that's another story.

Dumitru Ungureanu

unread,
Oct 17, 2012, 3:38:51 PM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
On Wednesday, October 17, 2012 10:09:19 PM UTC+3, chris dollin wrote: 
There is nothing that would "stop the i == 10 use".

Chris

--
Chris "allusive" Dollin

Not trying to attract another plethora of specs citations and closer or pointer explanations, but, put simply and abrasively "wrong", for my first post example:

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


I'd expect something like

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

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?

Andy Balholm

unread,
Oct 17, 2012, 3:46:54 PM10/17/12
to golan...@googlegroups.com, Andy Balholm
There is a difference in the machine code between global variables and local variables—although a "sufficiently smart" compiler could theoretically treat a global variable like a local variable if it were only used in one function, and that function were guaranteed to be called only once. 

There is often also a difference in the machine code between a variable local to one block and a variable local to another block. It's just that there was no difference in the particular program I showed.

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.

Jan Mercl

unread,
Oct 17, 2012, 3:48:14 PM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
On Wed, Oct 17, 2012 at 9:38 PM, Dumitru Ungureanu <itmi...@gmail.com> wrote:
> 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

Dumitru Ungureanu

unread,
Oct 17, 2012, 3:50:56 PM10/17/12
to golan...@googlegroups.com, Andy Balholm


On Wednesday, October 17, 2012 10:46:54 PM UTC+3, Andy Balholm wrote:
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.

My point is not this: annihilate 'i' when it reaches the value 10. My point is restrict 'i' use if/when it reaches the value 10, since it's local to a for loop with an explicit i < 10 condition for code execution.

Jan Mercl

unread,
Oct 17, 2012, 3:51:01 PM10/17/12
to Andy Balholm, golan...@googlegroups.com
On Wed, Oct 17, 2012 at 9:46 PM, Andy Balholm <andyb...@gmail.com> wrote:
> 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.

Taking address of something is, but once performed, pointees are
naturaly subject to no scoping.

-j

Andy Balholm

unread,
Oct 17, 2012, 3:53:02 PM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
On Wednesday, October 17, 2012 12:38:51 PM UTC-7, Dumitru Ungureanu wrote:
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?

As I mentioned before, the value of i after the loop finishes is always 10, closure or no closure:

var i int
for i = 0; i < 10; i++ {
}
fmt.Println("i =", i)

prints "i = 10"

There is really no such thing as a *valid* or *invalid* value of a loop variable:

for i := 0; i < 10; i++ {
i = 100
fmt.Println("i =", i)
}

will print "i = 100".

Jan Mercl

unread,
Oct 17, 2012, 3:53:14 PM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com, Andy Balholm
It is *not* local to the inside of your loop b/c you have captured it
in a closure! It's the same as if you would pass 'pi := &i' somwhere.
Then there 'i' can be accessed as '*pi', right?

-j

Andy Balholm

unread,
Oct 17, 2012, 3:54:29 PM10/17/12
to golan...@googlegroups.com, Andy Balholm
So what should this code do? panic?

var i int
for i = 0; i < 10; i++ {
}

Dumitru Ungureanu

unread,
Oct 17, 2012, 3:59:50 PM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
On Wednesday, October 17, 2012 10:48:44 PM UTC+3, Jan Mercl wrote:
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

They do work from "inside", otherwise it wouldn't be a closure.

It's clear that i has 10 for value at some point, and that it will maintain this value while being referenced someplace else, but it's interesting how/when this reference is maintained and how/when this value gets to be used.

Steven Blenkinsop

unread,
Oct 17, 2012, 4:03:52 PM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
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.

Dumitru Ungureanu

unread,
Oct 17, 2012, 4:06:10 PM10/17/12
to golan...@googlegroups.com, Andy Balholm
On Wednesday, October 17, 2012 10:54:29 PM UTC+3, Andy Balholm wrote:
So what should this code do? panic?

var i int
for i = 0; i < 10; i++ {
}
fmt.Println("i =", i) 

Certainly not. Here i is global, not local to the for loop.

This was the whole point, a variable local to a for loop, by means of closure, can transmit "illegal" values for that for loop to "outside" actors.

Dumitru Ungureanu

unread,
Oct 17, 2012, 4:11:20 PM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
On Wednesday, October 17, 2012 11:04:00 PM UTC+3, Steven Blenkinsop wrote:
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.

Let me try again.

If 'i' was a global variable, there was nothing interesting here.

But because 'i' is local to a for loop, yet, by means of closure, can transmit it's last residual increment value (10), to execute that closure within a statement situated *inside* the for's loop code block, not that's interesting, at least from a logic point of view, if explainable by Go's inner workings.

If it's not interesting, show me the same thing replicated in another language. And I don't mean "show me closures in another language". I mean the exact scenario, in the code in my first post.

Jan Mercl

unread,
Oct 17, 2012, 4:12:24 PM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com, Andy Balholm
On Wed, Oct 17, 2012 at 10:06 PM, Dumitru Ungureanu <itmi...@gmail.com> wrote:
> This was the whole point, a variable local to a for loop, by means of
> closure, can transmit "illegal" values for that for loop to "outside"
> actors.

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

Dumitru Ungureanu

unread,
Oct 17, 2012, 4:15:43 PM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu, Andy Balholm
On Wednesday, October 17, 2012 11:12:52 PM UTC+3, Jan Mercl wrote:
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

Would you say this is possible:

package main
func main() {
    for *i := 0; *i < 10; *i++ {
        // TODO
    }
}

Dumitru Ungureanu

unread,
Oct 17, 2012, 4:18:53 PM10/17/12
to golan...@googlegroups.com
And I don't mean this:

package main
func main() {
    var x int
    var i *int
    i = &x

Jan Mercl

unread,
Oct 17, 2012, 4:19:37 PM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com, Andy Balholm
On Wed, Oct 17, 2012 at 10:15 PM, Dumitru Ungureanu <itmi...@gmail.com> wrote:
> Would you say this is possible:
>
> package main
> func main() {
> for *i := 0; *i < 10; *i++ {
> // TODO
> }
> }

As that is invalid code I have no idea what it tries to communicate.

-j

Jan Mercl

unread,
Oct 17, 2012, 4:22:33 PM10/17/12
to Dumitru Ungureanu, golan...@googlegroups.com
On Wed, Oct 17, 2012 at 10:11 PM, Dumitru Ungureanu <itmi...@gmail.com> wrote:
> If it's not interesting, show me the same thing replicated in another
> language. And I don't mean "show me closures in another language". I mean
> the exact scenario, in the code in my first post.

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

Dumitru Ungureanu

unread,
Oct 17, 2012, 4:30:00 PM10/17/12
to golan...@googlegroups.com, Dumitru Ungureanu
On Wednesday, October 17, 2012 11:22:59 PM UTC+3, Jan Mercl wrote:
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

Are you sure this is a good example? Not much of a JS myself, but as I rememeber, variables are created by simple assignment, and are assumed global. My point is about local vars.

Dumitru Ungureanu

unread,
Oct 17, 2012, 4:31:03 PM10/17/12
to golan...@googlegroups.com
Run this:
It is loading more messages.
0 new messages