FAQ
I found a bug in my code, caused by ":=", which was very hard to find.
This is simplified example:

http://play.golang.org/p/nxthpeYv3p

In my real code Iter was a slice and Pop removes one element from it:

func (i Iter) Pop() (int, Iter) {
return i, i[1:]
}

The problem is because the intention was to get k and return updated
slice (don't change i), but compiled code gets k and redeclares i.

The ':=' properties cause that this code:

i := Iter(10)
for n := 0; n < 3; n++ {
k, i := i.Pop()
fmt.Println(k, i)
}

isn't expanded to:

var i Iter
i = 10
for n := 0; n < 3; n++ {
var (
i Iter
k int
)
k, i = i.Pop()
fmt.Println(k, i)
}

which in my case will be easier to debug, but it expands to

var i Iter = 10
for n := 0; n < 3; n++ {
var (
k Iter
tmpI int
)
k, tmpI = i.Pop()
var i = tmpI
fmt.Println(k, i)
}

which in real word sometime works sometime not.

go vet doesn't find this problem. It isn't probably too hard to check
it, so maybe it can be added to vet checks list.

--

Search Discussions

  • Rob Lapensee at Jan 21, 2013 at 1:13 pm
    If I understand the problem properly, I think this is a simpler example to
    illustrate:

    http://play.golang.org/p/FN8Yqwdn4N

    basically, if you have "b,a := 2,3" and "a" is already a defined variable,
    it will create a new shadow "a".

    so which is it:
    1) I don't understand the problem properly :)
    2) this is the way it is supposed to be and it is somewhere in the spec
    3) it works differently in tip
    4) it is not supposed to be this way

    Regards,

    Rob Lapensee
    On Monday, January 21, 2013 7:37:01 AM UTC-5, ziutek wrote:

    I found a bug in my code, caused by ":=", which was very hard to find.
    This is simplified example:

    http://play.golang.org/p/nxthpeYv3p

    In my real code Iter was a slice and Pop removes one element from it:

    func (i Iter) Pop() (int, Iter) {
    return i, i[1:]
    }

    The problem is because the intention was to get k and return updated
    slice (don't change i), but compiled code gets k and redeclares i.

    The ':=' properties cause that this code:

    i := Iter(10)
    for n := 0; n < 3; n++ {
    k, i := i.Pop()
    fmt.Println(k, i)
    }

    isn't expanded to:

    var i Iter
    i = 10
    for n := 0; n < 3; n++ {
    var (
    i Iter
    k int
    )
    k, i = i.Pop()
    fmt.Println(k, i)
    }

    which in my case will be easier to debug, but it expands to

    var i Iter = 10
    for n := 0; n < 3; n++ {
    var (
    k Iter
    tmpI int
    )
    k, tmpI = i.Pop()
    var i = tmpI
    fmt.Println(k, i)
    }

    which in real word sometime works sometime not.

    go vet doesn't find this problem. It isn't probably too hard to check
    it, so maybe it can be added to vet checks list.
    --
  • Chris dollin at Jan 21, 2013 at 1:39 pm

    On 21 January 2013 13:13, Rob Lapensee wrote:
    If I understand the problem properly, I think this is a simpler example to
    illustrate:

    http://play.golang.org/p/FN8Yqwdn4N

    basically, if you have "b,a := 2,3" and "a" is already a defined variable,
    it will create a new shadow "a".

    so which is it:
    1) I don't understand the problem properly :)
    2) this is the way it is supposed to be and it is somewhere in the spec This.
    3) it works differently in tip
    4) it is not supposed to be this way
    It's plain and simple lexical scoping.

    (As opposed to the less plain and simple redeclaration rule.)

    Chris

    --
    Chris "allusive" Dollin

    --
  • Ziutek at Jan 21, 2013 at 2:19 pm

    On 21 Sty, 14:13, Rob Lapensee wrote:
    If I understand the problem properly, I think this is a simpler example to
    illustrate:

    http://play.golang.org/p/FN8Yqwdn4N

    basically, if you have "b,a := 2,3" and "a" is already a defined variable,
    it will create a new shadow "a".
    This is very known ':=' property. You can easy and clearly convert
    "b,a := 2,3" to:

    var b, a int
    b, a = 2, 3

    We're all (Gophers) used to it (more or less).

    I meant more subtle case, like:

    b, a := a.Method()

    where a is declared outside of current scope.

    --
  • Minux at Jan 21, 2013 at 1:29 pm

    On Mon, Jan 21, 2013 at 8:37 PM, ziutek wrote:

    I found a bug in my code, caused by ":=", which was very hard to find.
    This is simplified example:

    http://play.golang.org/p/nxthpeYv3p

    In my real code Iter was a slice and Pop removes one element from it:

    func (i Iter) Pop() (int, Iter) {
    return i, i[1:]
    }

    The problem is because the intention was to get k and return updated
    slice (don't change i), but compiled code gets k and redeclares i.

    The ':=' properties cause that this code:

    i := Iter(10)
    for n := 0; n < 3; n++ {
    k, i := i.Pop()
    Go uses lexical scoping rule, which means for variable references you only
    need to find the
    lexically nearest definition of i (that is, nearest definition before the
    current reference point).

    if it used the last definition of i, it will be like dynamic scoping and
    IMO that will be even more
    confusing.
    fmt.Println(k, i)
    }

    isn't expanded to:

    var i Iter
    i = 10
    for n := 0; n < 3; n++ {
    var (
    i Iter
    k int
    )
    k, i = i.Pop()
    fmt.Println(k, i)
    }

    which in my case will be easier to debug, but it expands to

    var i Iter = 10
    for n := 0; n < 3; n++ {
    var (
    k Iter
    tmpI int
    )
    k, tmpI = i.Pop()
    var i = tmpI
    fmt.Println(k, i)
    }

    which in real word sometime works sometime not.

    go vet doesn't find this problem. It isn't probably too hard to check
    it, so maybe it can be added to vet checks list.
    --
  • Michal at Jan 21, 2013 at 2:24 pm
    This is strange also:
    http://play.golang.org/p/Uuc1IFlJhM

    W dniu poniedziałek, 21 stycznia 2013 13:37:01 UTC+1 użytkownik ziutek
    napisał:
    I found a bug in my code, caused by ":=", which was very hard to find.
    This is simplified example:

    http://play.golang.org/p/nxthpeYv3p

    In my real code Iter was a slice and Pop removes one element from it:

    func (i Iter) Pop() (int, Iter) {
    return i, i[1:]
    }

    The problem is because the intention was to get k and return updated
    slice (don't change i), but compiled code gets k and redeclares i.

    The ':=' properties cause that this code:

    i := Iter(10)
    for n := 0; n < 3; n++ {
    k, i := i.Pop()
    fmt.Println(k, i)
    }

    isn't expanded to:

    var i Iter
    i = 10
    for n := 0; n < 3; n++ {
    var (
    i Iter
    k int
    )
    k, i = i.Pop()
    fmt.Println(k, i)
    }

    which in my case will be easier to debug, but it expands to

    var i Iter = 10
    for n := 0; n < 3; n++ {
    var (
    k Iter
    tmpI int
    )
    k, tmpI = i.Pop()
    var i = tmpI
    fmt.Println(k, i)
    }

    which in real word sometime works sometime not.

    go vet doesn't find this problem. It isn't probably too hard to check
    it, so maybe it can be added to vet checks list.
    --
  • Chris dollin at Jan 21, 2013 at 2:26 pm

    On 21 January 2013 14:24, wrote:
    This is strange also:
    http://play.golang.org/p/Uuc1IFlJhM
    Why is it strange? It looks like the behaviour I'd
    expect.

    Chris

    --
    Chris "allusive" Dollin

    --
  • Jan Mercl at Jan 21, 2013 at 2:34 pm

    On Mon, Jan 21, 2013 at 3:24 PM, wrote:
    This is strange also:
    http://play.golang.org/p/Uuc1IFlJhM
    Try an excercise: http://play.golang.org/p/0BqJ9QNYkP

    Which `x` gets printed? ;-)

    -j

    --
  • Martin Angers at Jan 21, 2013 at 2:45 pm
    Le lundi 21 janvier 2013 09:24:21 UTC-5, mic...@zeto.wroc.pl a écrit :

    To me this is actually expected. Lexical scoping, as others mentioned. Your
    "i" gets redefined in the enclosing block. In the "for" it is always
    initialized with the value of the outer "i" minus one. It can be confusing
    if you come from, say, javascript, which only has function-level scope, but
    for C-like languages with block scoping, this is pretty standard.

    --
  • Ziutek at Jan 21, 2013 at 3:02 pm

    On 21 Sty, 15:45, Martin Angers wrote:
    Le lundi 21 janvier 2013 09:24:21 UTC-5, mic...@zeto.wroc.pl a écrit :


    To me this is actually expected. Lexical scoping, as others mentioned. Your
    "i" gets redefined in the enclosing block. In the "for" it is always
    initialized with the value of the outer "i" minus one. It can be confusing
    if you come from, say, javascript, which only has function-level scope, but
    for C-like languages with block scoping, this is pretty standard.
    I am happy with ':=' properties (I'm programing in Go since 2010). I
    only suggest that would be nice if go vet warn me when sees such code:

    sl := pmt.ESInfo()
    for len(sl) > 0 {
    i, sl := sl.Pop()
    // ...
    }

    because I'm not a machine, so I make such mistakes sometimes.

    --
  • Nate Finch at Jan 21, 2013 at 4:13 pm
    The problem with having vet warn you about this is that this is actually a
    common pattern for many programmers, and to warn every time would be too
    spammy.
    On Jan 21, 2013 10:03 AM, "ziutek" wrote:
    On 21 Sty, 15:45, Martin Angers wrote:
    Le lundi 21 janvier 2013 09:24:21 UTC-5, mic...@zeto.wroc.pl a écrit :


    To me this is actually expected. Lexical scoping, as others mentioned. Your
    "i" gets redefined in the enclosing block. In the "for" it is always
    initialized with the value of the outer "i" minus one. It can be confusing
    if you come from, say, javascript, which only has function-level scope, but
    for C-like languages with block scoping, this is pretty standard.
    I am happy with ':=' properties (I'm programing in Go since 2010). I
    only suggest that would be nice if go vet warn me when sees such code:

    sl := pmt.ESInfo()
    for len(sl) > 0 {
    i, sl := sl.Pop()
    // ...
    }

    because I'm not a machine, so I make such mistakes sometimes.

    --

    --
  • Dmitri Shuralyov at Jan 21, 2013 at 7:11 pm
    This assumes existing command-line tools, correct?

    I mean, if it could be done differently so that it would only warn you once
    per new occurrence (and you could mark it as 'ignore this warning' or
    'ignore all warnings like this'), would that be more acceptable?

    On Monday, January 21, 2013 11:13:23 AM UTC-5, Nate Finch wrote:

    The problem with having vet warn you about this is that this is actually a
    common pattern for many programmers, and to warn every time would be too
    spammy.
    On Jan 21, 2013 10:03 AM, "ziutek" <ziu...@lnet.pl <javascript:>> wrote:
    On 21 Sty, 15:45, Martin Angers wrote:
    Le lundi 21 janvier 2013 09:24:21 UTC-5, mic...@zeto.wroc.pl a écrit :


    To me this is actually expected. Lexical scoping, as others mentioned. Your
    "i" gets redefined in the enclosing block. In the "for" it is always
    initialized with the value of the outer "i" minus one. It can be confusing
    if you come from, say, javascript, which only has function-level scope, but
    for C-like languages with block scoping, this is pretty standard.
    I am happy with ':=' properties (I'm programing in Go since 2010). I
    only suggest that would be nice if go vet warn me when sees such code:

    sl := pmt.ESInfo()
    for len(sl) > 0 {
    i, sl := sl.Pop()
    // ...
    }

    because I'm not a machine, so I make such mistakes sometimes.

    --

    --
  • Rui Maciel at Jan 21, 2013 at 8:00 pm

    On 01/21/2013 07:11 PM, Dmitri Shuralyov wrote:
    This assumes existing command-line tools, correct?

    I mean, if it could be done differently so that it would only warn you once
    per new occurrence (and you could mark it as 'ignore this warning' or
    'ignore all warnings like this'), would that be more acceptable?
    I believe it wouldn't be acceptable because, as Nate said, that tends to
    be exactly what people expect from a short variable declaration.

    It's nice to get an empty error/warning list when the compiler/source
    code analyzer finishes its run. It's positive reinforcement! We
    wouldn't get that if a perfectly valid construct, which is both
    perfectly legal and was used as intended, ends up being flagged as a
    possible error.

    It's better to simply learn how the language actually works instead of
    demanding that it should be forced to adapt to our expectations. This
    is one of those cases, because Go's rules for variable declarations are
    quite simple and straightforward.


    Rui Maciel

    --
  • Patrick Mylund Nielsen at Jan 21, 2013 at 8:11 pm
    The same way anyone can program in C++ if they just learn the language
    because the rules are clearly defined. Lay off the condescension, will you.
    No problem with having "go vet" warn about code that compiles, but might
    not produce the result you wanted--it's what it's there for.

    On Mon, Jan 21, 2013 at 3:00 PM, Rui Maciel wrote:
    On 01/21/2013 07:11 PM, Dmitri Shuralyov wrote:

    This assumes existing command-line tools, correct?

    I mean, if it could be done differently so that it would only warn you
    once
    per new occurrence (and you could mark it as 'ignore this warning' or
    'ignore all warnings like this'), would that be more acceptable?
    I believe it wouldn't be acceptable because, as Nate said, that tends to
    be exactly what people expect from a short variable declaration.

    It's nice to get an empty error/warning list when the compiler/source code
    analyzer finishes its run. It's positive reinforcement! We wouldn't get
    that if a perfectly valid construct, which is both perfectly legal and was
    used as intended, ends up being flagged as a possible error.

    It's better to simply learn how the language actually works instead of
    demanding that it should be forced to adapt to our expectations. This is
    one of those cases, because Go's rules for variable declarations are quite
    simple and straightforward.


    Rui Maciel

    --

    --
  • Rui Maciel at Jan 21, 2013 at 9:20 pm

    On 01/21/2013 08:11 PM, Patrick Mylund Nielsen wrote:
    The same way anyone can program in C++ if they just learn the language
    because the rules are clearly defined. Lay off the condescension, will you.
    No problem with having "go vet" warn about code that compiles, but might
    not produce the result you wanted--it's what it's there for.
    Could you please quote exactly which bit in my post sounds condescending
    to you? Meanwhile, if you are that sensible towards condescending
    messages what lead you to believe that spending your time writing such
    condescending messages would make things better?

    Regarding your comment, that's a gross and misleading exaggeration.
    Being aware that in Go a variable declaration results in a variable
    being declared is not exactly at the same level of complexity as
    learning a language known for its extreme complexity which actually
    tends not to be clearly defined.

    And with Go, what result do you expect from a variable declaration,
    other than a variable actually being declared? Essentially all replies
    to this thread point out that that's precisely the behavior which is
    expected, and the only one. Do you disagree with this?


    Rui Maciel

    --
  • Patrick Mylund Nielsen at Jan 21, 2013 at 9:23 pm
    Could you please quote exactly which bit in my post sounds condescending
    to you?

    Telling somebody who has been a member of the community for years that he
    needs to just "learn the language" when he raises a very valid point.
    Shadowing by := has come up many times. There is no reason why "go get"
    can't warn when you are shadowing variables, just like the compiler now
    rejects shadowing return variables.
    Do you disagree with this?
    I disagree that it's obvious and that there is no basis for having a tool
    that's supposed to warn you about unintended behavior warn about it.

    On Mon, Jan 21, 2013 at 4:15 PM, Rui Maciel wrote:
    On 01/21/2013 08:11 PM, Patrick Mylund Nielsen wrote:

    The same way anyone can program in C++ if they just learn the language
    because the rules are clearly defined. Lay off the condescension, will
    you.
    No problem with having "go vet" warn about code that compiles, but might
    not produce the result you wanted--it's what it's there for.
    Could you please quote exactly which bit in my post sounds condescending
    to you? Meanwhile, if you are that sensible towards condescending messages
    what lead you to believe that spending your time writing such condescending
    messages would make things better?

    Regarding your comment, that's a gross and misleading exaggeration. Being
    aware that in Go a variable declaration results in a variable being
    declared is not exactly at the same level of complexity as learning a
    language known for its extreme complexity which actually tends not to be
    clearly defined.

    And with Go, what result do you expect from a variable declaration, other
    than a variable actually being declared? Essentially all replies to this
    thread point out that that's precisely the behavior which is expected, and
    the only one. Do you disagree with this?


    Rui Maciel
    --
  • Patrick Mylund Nielsen at Jan 21, 2013 at 9:23 pm
    s/go get/go vet/

    On Mon, Jan 21, 2013 at 4:23 PM, Patrick Mylund Nielsen wrote:

    Could you please quote exactly which bit in my post sounds
    condescending to you?

    Telling somebody who has been a member of the community for years that he
    needs to just "learn the language" when he raises a very valid point.
    Shadowing by := has come up many times. There is no reason why "go get"
    can't warn when you are shadowing variables, just like the compiler now
    rejects shadowing return variables.
    Do you disagree with this?
    I disagree that it's obvious and that there is no basis for having a tool
    that's supposed to warn you about unintended behavior warn about it.

    On Mon, Jan 21, 2013 at 4:15 PM, Rui Maciel wrote:
    On 01/21/2013 08:11 PM, Patrick Mylund Nielsen wrote:

    The same way anyone can program in C++ if they just learn the language
    because the rules are clearly defined. Lay off the condescension, will
    you.
    No problem with having "go vet" warn about code that compiles, but might
    not produce the result you wanted--it's what it's there for.
    Could you please quote exactly which bit in my post sounds condescending
    to you? Meanwhile, if you are that sensible towards condescending messages
    what lead you to believe that spending your time writing such condescending
    messages would make things better?

    Regarding your comment, that's a gross and misleading exaggeration. Being
    aware that in Go a variable declaration results in a variable being
    declared is not exactly at the same level of complexity as learning a
    language known for its extreme complexity which actually tends not to be
    clearly defined.

    And with Go, what result do you expect from a variable declaration, other
    than a variable actually being declared? Essentially all replies to this
    thread point out that that's precisely the behavior which is expected, and
    the only one. Do you disagree with this?


    Rui Maciel
    --
  • Rui Maciel at Jan 22, 2013 at 2:37 am

    On 01/21/2013 09:23 PM, Patrick Mylund Nielsen wrote:
    Could you please quote exactly which bit in my post sounds condescending
    to you?

    Telling somebody who has been a member of the community for years that he
    needs to just "learn the language" when he raises a very valid point.
    I don't understand what you tried to say. Do you know all there is to
    know about a programming language? Do you believe it's possible and
    expected that anyone is able to attain such a level of flawless mastery
    in a couple of years? If you don't believe that then certainly you
    agree that it's possible that, even with some experience, it's possible
    that something about the programming language was able to fly under the
    radar. If you agree with that then certainly you understand that its
    quite natural that even after a couple of years it's still quite
    possible to still be able to learn a thing or two.

    Moreover, everyone makes mistakes. I know I do, even in areas I have
    quite a bit more than a couple of years of experience. If someone makes
    one, asks for help and you happen to know not only what causes the
    problem but also how it can be solved, will you actually help them out
    or do you expect to see a cv to figure out if your reply will be
    condescending or not?

    Shadowing by := has come up many times. There is no reason why "go get"
    can't warn when you are shadowing variables, just like the compiler now
    rejects shadowing return variables.
    What compiler version are you talking about? The following example
    works just fine both in the Go playground and with the version of Go
    made available through Ubuntu's repositories (golang_1-5_all.deb,
    Version: 2:1-5)

    http://play.golang.org/p/AoR5_nk7Aw


    This bug report might be of some interest:

    http://code.google.com/p/go/issues/detail?id=377&q=shadow%20return%20variable&colspec=ID%20Status%20Stars%20Priority%20Owner%20Reporter%20Summary

    Can you provide an example where an attempt to shadow return variables
    triggers an error?

    Do you disagree with this?
    I disagree that it's obvious and that there is no basis for having a tool
    that's supposed to warn you about unintended behavior warn about it.
    See the bug report I've pasted above.


    Rui Maciel

    --
  • Patrick Mylund Nielsen at Jan 22, 2013 at 2:40 am
    http://golang.org/doc/go1.html#shadowing

    On Mon, Jan 21, 2013 at 9:30 PM, Rui Maciel wrote:


    On 01/21/2013 09:23 PM, Patrick Mylund Nielsen wrote:

    Could you please quote exactly which bit in my post sounds condescending
    to you?

    Telling somebody who has been a member of the community for years that he
    needs to just "learn the language" when he raises a very valid point.
    I don't understand what you tried to say. Do you know all there is to
    know about a programming language? Do you believe it's possible and
    expected that anyone is able to attain such a level of flawless mastery in
    a couple of years? If you don't believe that then certainly you agree that
    it's possible that, even with some experience, it's possible that something
    about the programming language was able to fly under the radar. If you
    agree with that then certainly you understand that its quite natural that
    even after a couple of years it's still quite possible to still be able to
    learn a thing or two.

    Moreover, everyone makes mistakes. I know I do, even in areas I have
    quite a bit more than a couple of years of experience. If someone makes
    one, asks for help and you happen to know not only what causes the problem
    but also how it can be solved, will you actually help them out or do you
    expect to see a cv to figure out if your reply will be condescending or not?



    Shadowing by := has come up many times. There is no reason why "go get"
    can't warn when you are shadowing variables, just like the compiler now
    rejects shadowing return variables.
    What compiler version are you talking about? The following example works
    just fine both in the Go playground and with the version of Go made
    available through Ubuntu's repositories (golang_1-5_all.deb, Version: 2:1-5)

    http://play.golang.org/p/AoR5_**nk7Aw<http://play.golang.org/p/AoR5_nk7Aw>


    This bug report might be of some interest:

    http://code.google.com/p/go/**issues/detail?id=377&q=shadow%**
    20return%20variable&colspec=**ID%20Status%20Stars%**
    20Priority%20Owner%20Reporter%**20Summary<http://code.google.com/p/go/issues/detail?id=377&q=shadow%20return%20variable&colspec=ID%20Status%20Stars%20Priority%20Owner%20Reporter%20Summary>

    Can you provide an example where an attempt to shadow return variables
    triggers an error?



    Do you disagree with this?
    I disagree that it's obvious and that there is no basis for having a tool
    that's supposed to warn you about unintended behavior warn about it.
    See the bug report I've pasted above.


    Rui Maciel
    --
  • Rui Maciel at Jan 22, 2013 at 2:59 am

    On 01/22/2013 02:40 AM, Patrick Mylund Nielsen wrote:
    http://golang.org/doc/go1.html#shadowing
    Read the section you've quoted. It clearly states that it's about
    calling return without arguments, not about returning shadowed
    variables. Contrary to what you've claimed, returning shadowed
    variables is perfectly fine.

    http://play.golang.org/p/7B8kJ6AZFD



    Rui Maciel

    --
  • Patrick Mylund Nielsen at Jan 22, 2013 at 3:23 am
    *sigh* This will be my last reply to your comments.

    I said "go vet" should give a warning, just like the compiler does when you
    return shadowed variables. This does not mean that the two scenarios are
    identical. If they were, we wouldn't need "go vet" to warn about it--you
    wouldn't be able to compile the code.

    On Mon, Jan 21, 2013 at 9:58 PM, Rui Maciel wrote:
    Read the section you've quoted. It clearly states that it's about calling
    return without arguments, not about returning shadowed variables. Contrary
    to what you've claimed, returning shadowed variables is perfectly fine.

    http://play.golang.org/p/**7B8kJ6AZFD<http://play.golang.org/p/7B8kJ6AZFD>



    Rui Maciel
    --
  • Sean Russell at Jan 22, 2013 at 10:52 am

    On Monday, January 21, 2013 10:23:17 PM UTC-5, Patrick Mylund Nielsen wrote:
    I said "go vet" should give a warning, just like the compiler does when
    you return shadowed variables.
    I've seen no evidence that anybody in the core dev team pays attention to
    "+1," but in case they do:

    +1 for "go vet" reporting this case.

    --- SER

    --
  • John Nagle at Jan 23, 2013 at 7:50 am

    On 1/21/2013 6:40 PM, Patrick Mylund Nielsen wrote:
    http://golang.org/doc/go1.html#shadowing
    I like the comment there that when the shadowing rules were last
    tightened "The few cases that arose in the standard repository were
    mostly bugs."

    John Nagle

    --
  • Jan Mercl at Jan 23, 2013 at 8:19 am

    On Wed, Jan 23, 2013 at 8:50 AM, John Nagle wrote:
    On 1/21/2013 6:40 PM, Patrick Mylund Nielsen wrote:
    http://golang.org/doc/go1.html#shadowing
    I like the comment there that when the shadowing rules were last
    tightened "The few cases that arose in the standard repository were
    mostly bugs."
    Let me add necessary context. The quoted sentence talks exclusively
    about named return variables (the section "Returns and shadowed
    variables"). And, probably even more important, the comment is about
    changes wrt the pre Go 1 (i.e. the experimental) version of Go.

    -j

    --
  • Jens Alfke at Jan 21, 2013 at 9:27 pm

    On Jan 21, 2013, at 1:15 PM, Rui Maciel wrote:

    And with Go, what result do you expect from a variable declaration, other than a variable actually being declared? Essentially all replies to this thread point out that that's precisely the behavior which is expected, and the only one. Do you disagree with this?
    Yes, I do. If := always declared variables, you’d have a point. But it doesn’t. If there are multiple variables in the LHS and some are already declared in the current scope, they get reused. So a multi-value := statement can validly contain a mixture of declaration and simple assignment. (And this mixture is very, very common in real code due to the convention of returning errors as a second return value, where the variable ‘err’ gets reused.)

    The argument being made here is that this can result in confusing code where variables are shadowed unintentionally. Several people (in this thread, and in my workplace) report running into this often enough that it becomes a pain point in the language. In this case it is not helpful to reiterate that the syntax is unambiguous — of course it is, because a machine is parsing it. It’s easy to create arbitrarily confusing and difficult-to-use syntaxes that are nonetheless unambiguously parseable (brainf*ck is a fine example.)

    —Jens

    --
  • Patrick Mylund Nielsen at Jan 21, 2013 at 9:42 pm
    Yes, I do. If := always declared variables, you’d have a point. But it
    doesn’t. If there are multiple variables in the LHS and some are already
    declared in the current scope, they get reused. So a multi-value :=
    statement can validly contain a mixture of declaration and simple
    assignment.

    Judging from Rui's statement that := "always declares" rather than
    "redeclares" (assigns), it seems he didn't actually realize that := behaves
    this way, which adds to the argument that the behavior _isn't_ obvious, and
    that, in fact, it would be a good idea to have go vet warn the user when
    they might have used it incorrectly.

    http://golang.org/ref/spec#Short_variable_declarations

    "Unlike regular variable declarations, a short variable declaration may
    redeclare variables provided they were originally declared in the same
    block with the same type, and at least one of the non-blank variables is
    new. As a consequence, redeclaration can only appear in a multi-variable
    short declaration. Redeclaration does not introduce a new variable; it just
    assigns a new value to the original."

    On Mon, Jan 21, 2013 at 4:27 PM, Jens Alfke wrote:


    On Jan 21, 2013, at 1:15 PM, Rui Maciel wrote:

    And with Go, what result do you expect from a variable declaration, other
    than a variable actually being declared? Essentially all replies to this
    thread point out that that's precisely the behavior which is expected, and
    the only one. Do you disagree with this?


    Yes, I do. If := always declared variables, you’d have a point. But it
    doesn’t. If there are multiple variables in the LHS and some are already
    declared in the current scope, they get reused. So a multi-value :=
    statement can validly contain a mixture of declaration and simple
    assignment. (And this mixture is very, very common in real code due to the
    convention of returning errors as a second return value, where the variable
    ‘err’ gets reused.)

    The argument being made here is that this can result in confusing code
    where variables are shadowed unintentionally. Several people (in this
    thread, and in my workplace) report running into this often enough that it
    becomes a pain point in the language. In this case it is not helpful to
    reiterate that the syntax is unambiguous — of course it is, because a
    machine is parsing it. It’s easy to create arbitrarily confusing and
    difficult-to-use syntaxes that are nonetheless unambiguously parseable
    (brainf*ck is a fine example.)

    —Jens
    --
  • Rui Maciel at Jan 22, 2013 at 2:50 am

    On 01/21/2013 09:42 PM, Patrick Mylund Nielsen wrote:
    Judging from Rui's statement that := "always declares" rather than
    "redeclares" (assigns), it seems he didn't actually realize that := behaves
    this way
    Not quite. Instead, by your comment it became clear that you are a bit
    confused. See bellow.


    which adds to the argument that the behavior_isn't_ obvious, and
    that, in fact, it would be a good idea to have go vet warn the user when
    they might have used it incorrectly.

    http://golang.org/ref/spec#Short_variable_declarations

    "Unlike regular variable declarations, a short variable declaration may
    redeclare variables provided they were originally declared in the same
    block with the same type, and at least one of the non-blank variables is
    new. As a consequence, redeclaration can only appear in a multi-variable
    short declaration. Redeclaration does not introduce a new variable; it just
    assigns a new value to the original."

    Did you noticed the bit in your quote that reads "provided they were
    originally declared in the same block"? That's not the case the OP has
    presented, nor what has been talked about on this entire thread.
    Instead, this whole thread has been about hiding variables by
    redeclaring the identifier (and not the variable) within a inner block.
    Take a look at what's been said in the thread, particularly the post
    you've replied to with your condescending post.

    Maybe you should actually read what has been said instead of assuming
    something entirely different and then proceeding to attack others based
    on that vague notion.


    Rui Maciel

    --
  • Patrick Mylund Nielsen at Jan 22, 2013 at 3:17 am
    You claimed := always declares (i.e. always introduces a new variable, so
    its behavior is obvious. If that's not what you meant, then that claim was
    as intuitive as its behavior) It was showed that it doesn't. That's all.

    You also claimed that everyone agrees and understands how :=/shadowing
    works. That is not the case. In fact, several have mentioned that they've
    been caught by this numerous time. Logic dictates that a tool designed to
    point out subtle errors that are easy to make even for experienced
    programmers should point out such a class of errors if a large enough
    percentage of people experience them.
    your condescending post.
    see what I mean? There is a growing number of posters here who are quick to
    tell people to "just learn the language" if they bring up perfectly
    understandable, and perfectly reasonable, problems and propositions. Adding
    a check to "go vet" does not constitute changing the language, and may help
    many people. Why fight it so hard with "GO IS BEST LALALALALLALALA" when
    several people have admitted to stumbling over/being confused by the same
    behavior?

    I will admit that I'm probably not clever enough to implement such a check,
    but I will try to play around with it at some point.

    On Mon, Jan 21, 2013 at 9:50 PM, Rui Maciel wrote:
    On 01/21/2013 09:42 PM, Patrick Mylund Nielsen wrote:


    Judging from Rui's statement that := "always declares" rather than
    "redeclares" (assigns), it seems he didn't actually realize that :=
    behaves
    this way
    Not quite. Instead, by your comment it became clear that you are a bit
    confused. See bellow.


    which adds to the argument that the behavior_isn't_ obvious, and

    that, in fact, it would be a good idea to have go vet warn the user when
    they might have used it incorrectly.

    http://golang.org/ref/spec#**Short_variable_declarations<http://golang.org/ref/spec#Short_variable_declarations>

    "Unlike regular variable declarations, a short variable declaration may
    redeclare variables provided they were originally declared in the same
    block with the same type, and at least one of the non-blank variables is
    new. As a consequence, redeclaration can only appear in a multi-variable
    short declaration. Redeclaration does not introduce a new variable; it
    just
    assigns a new value to the original."

    Did you noticed the bit in your quote that reads "provided they were
    originally declared in the same block"? That's not the case the OP has
    presented, nor what has been talked about on this entire thread. Instead,
    this whole thread has been about hiding variables by redeclaring the
    identifier (and not the variable) within a inner block. Take a look at
    what's been said in the thread, particularly the post you've replied to
    with your condescending post.

    Maybe you should actually read what has been said instead of assuming
    something entirely different and then proceeding to attack others based on
    that vague notion.


    Rui Maciel
    --
  • Rui Maciel at Jan 22, 2013 at 2:37 am

    On 01/21/2013 09:27 PM, Jens Alfke wrote:
    Yes, I do. If := always declared variables, you’d have a point. But
    it doesn’t. If there are multiple variables in the LHS and some are
    already declared in the current scope, they get reused.
    OP's example, as well as this whole thread, has been about a very
    specific case: declaring a variable through a short variable declaration
    within a inner block, and in the process hiding a variable. This is the
    case which has been discussed from the start of this thread. In this
    case, the short variable declaration does not reuse the variable, and
    instead declares a new entity. This is what has been discussed here, at
    least until now. Hence, the post you've replied to.

    With regards to the generic behavior of Go's short variable declaration,
    you are right: if there are multiple variables and a subset of them has
    been declared previously in the same block then the undeclared variables
    will be declared and, as a convenient exception, the ones already
    declared will be redeclared, "provided they were originally declared in
    the same block with the same type"[1].

    Yet, that is a convenient exception to the rule, but not the rule. If
    any of those requirements isn't met then the code isn't valid, and the
    compiler will throw an error. If the short variable declaration is used
    without declaring a single variable (i.e., as a assignment operator)
    then the code is broken and the compiler throws an error.

    http://play.golang.org/p/y6vfSKvUyk


    That's because what the short variable declaration does is declare
    variables, which in a very specific corner case also assigns a new value
    to variables which have been declared previously. It is not an
    assignment operator, which as a corner case also declares variables.

    So, even when considering this corner case, the problem doesn't lie in
    expecting that this way of declaring variables ends up declaring
    variables, because that's not the exception to the rule but the rule itself.

    So a
    multi-value := statement can validly contain a mixture of declaration
    and simple assignment. (And this mixture is very, very common in real
    code due to the convention of returning errors as a second return
    value, where the variable ‘err’ gets reused.)
    This is true. Yet, again, it's an exception, which happens to be very
    convenient. But just because, as an exception, this way of declaring
    variables also supports a way to redeclare previously declared
    variables, this doesn't mean that a short variable declaration is
    actually an assignment operator. As I've pointed out previously, if a
    short variable declaration is used in place of an assignment operator,
    the compiler throws an error.

    The argument being made here is that this can result in confusing
    code where variables are shadowed unintentionally. Several people (in
    this thread, and in my workplace) report running into this often
    enough that it becomes a pain point in the language. In this case it
    is not helpful to reiterate that the syntax is unambiguous — of
    course it is, because a machine is parsing it. It’s easy to create
    arbitrarily confusing and difficult-to-use syntaxes that are
    nonetheless unambiguously parseable (brainf*ck is a fine example.)
    My point was that, in the case presented by the OP, this is only
    confusing due to a single misconception which can be easily understood.
    More precisely, if we acknowledge that a variable declaration within a
    inner block does declare a new variable and, in the process, hides any
    variable declared previously that shares the identifier[2] then we won't
    be surprised if that's what we get when we do precisely that. For
    example, take the following examples:

    <pseudosource>
    var a int = 1
    {
    var a int = 0
    fmt.Print(a)
    }
    fmt.Print(a)
    </pseudosource>

    <pseudosource>
    var a int = 1
    var a int = 0
    </pseudosource>


    If the first example isn't expected to triggers a compiler error while
    the second one is expected to do so, and we understand why, then that's
    more than enough to understand what happens with:


    <pseudocode>
    a := 1
    {
    a := 0
    fmt.Print(a)
    }
    fmt.Println(a)
    </pseudocode>

    Is this confusing?


    Rui Maciel

    [1] http://golang.org/ref/spec#Short_variable_declarations
    [2] http://golang.org/ref/spec#Declarations_and_scope

    --
  • Jens Alfke at Jan 21, 2013 at 8:27 pm

    On Monday, January 21, 2013 12:00:52 PM UTC-8, Rui Maciel wrote:
    It's nice to get an empty error/warning list when the compiler/source
    code analyzer finishes its run. It's positive reinforcement! We
    wouldn't get that if a perfectly valid construct, which is both
    perfectly legal and was used as intended, ends up being flagged as a
    possible error.
    C/C++ compilers have plenty of warnings about constructs that are legal but
    frequently used accidentally due to typos or think-os. For example, GCC and
    Clang warn about "if (x = foo()) ..." because it's quite common for this to
    result from forgetting to type the second "=".


    It's better to simply learn how the language actually works instead of
    demanding that it should be forced to adapt to our expectations.
    Completely disagree. All high-level languages are attempts to adapt
    computer programs to the way humans think. I can imagine people in the '50s
    using arguments like yours to say that FORTRAN shouldn't have operator
    precedence, all operators should just be parsed left to right and
    programmers should just learn how the language works. :)

    In UI design it's a truism (at least at Apple) that, if a lot of users have
    trouble figuring out a feature, it's because you've designed the feature
    wrong, not because they're stupid and need to learn the program better.

    --Jens

    --
  • Martin Angers at Jan 21, 2013 at 4:44 pm
    I do agree with that, though. It would be nice to have a way to be warned
    if you shadow another variable (I think this came up a while ago in another
    thread). And to do it not by the compiler (no warnings, just errors) but by
    go vet seems a good idea, maybe with a flag or something.

    Le lundi 21 janvier 2013 10:02:54 UTC-5, ziutek a écrit :
    On 21 Sty, 15:45, Martin Angers wrote:
    Le lundi 21 janvier 2013 09:24:21 UTC-5, mic...@zeto.wroc.pl a écrit :


    To me this is actually expected. Lexical scoping, as others mentioned. Your
    "i" gets redefined in the enclosing block. In the "for" it is always
    initialized with the value of the outer "i" minus one. It can be confusing
    if you come from, say, javascript, which only has function-level scope, but
    for C-like languages with block scoping, this is pretty standard.
    I am happy with ':=' properties (I'm programing in Go since 2010). I
    only suggest that would be nice if go vet warn me when sees such code:

    sl := pmt.ESInfo()
    for len(sl) > 0 {
    i, sl := sl.Pop()
    // ...
    }

    because I'm not a machine, so I make such mistakes sometimes.
    --
  • Jens Alfke at Jan 21, 2013 at 8:18 pm

    On Monday, January 21, 2013 8:44:40 AM UTC-8, Martin Angers wrote:
    I do agree with that, though. It would be nice to have a way to be warned
    if you shadow another variable
    +1. I understand lexical scoping, but in my experience shadowing variables
    (at least in C-like languages) is generally considered a Very Bad Thing as
    it is easily done by accident, is rather difficult to notice, and can
    result in nasty and mysterious bugs.

    I consider it a serious misfeature of Go that the := operator, given a
    choice whether to reuse an enclosing-scope variable or create a shadow,
    decides to create a shadow. It's even worse since that's different than
    what := does if the variable pre-exists in the current (not enclosing)
    scope:

    var x ....
    x, y := foo() // reuses existing x, as expected

    versus

    var x ....
    {
    x, y := foo() // creates new x shadowing outer one -- bad!
    }
    // at this point my code expected x to be changed but it's not

    I've been writing a lot of Go code for about four months now, so I've had
    chance to get used to its way of doing things. At this point, this is the
    #1 thing I think is flat-out wrong in the language.

    --Jens

    --
  • Jan Mercl at Jan 21, 2013 at 8:37 pm

    On Mon, Jan 21, 2013 at 9:18 PM, Jens Alfke wrote:
    var x ....
    x, y := foo() // reuses existing x, as expected

    versus

    var x ....
    {
    x, y := foo() // creates new x shadowing outer one -- bad!
    }
    // at this point my code expected x to be changed but it's not
    That's a bad expectation. Block scoping is not bad. Block scope is
    good for what it is designed. It's bad when only used erroneously.

    Consider for example moving a bracketed block of code (and it's
    variables, ...) to some other place. If it did not referenced outer
    scope variables, it just work in the new place with no "identifier
    already defined" troubles.

    -j

    --
  • John Nagle at Jan 21, 2013 at 9:28 pm

    On 1/21/2013 12:18 PM, Jens Alfke wrote:

    On Monday, January 21, 2013 8:44:40 AM UTC-8, Martin Angers wrote:

    I do agree with that, though. It would be nice to have a way to be warned
    if you shadow another variable
    +1. I understand lexical scoping, but in my experience shadowing variables
    (at least in C-like languages) is generally considered a Very Bad Thing as
    it is easily done by accident, is rather difficult to notice, and can
    result in nasty and mysterious bugs.

    I consider it a serious misfeature of Go that the := operator, given a
    choice whether to reuse an enclosing-scope variable or create a shadow,
    decides to create a shadow. It's even worse since that's different than
    what := does if the variable pre-exists in the current (not enclosing)
    scope:
    It's especially tricky because of the rule that ":=", given at least
    one variable to be defined, allows others in the same statement to
    merely be assigned. That's a new feature in Go, not one used in
    other languages.

    I'd argue against allowing the shadowing of variables though implicit
    definitions. If you really want to shadow (which is rare), you should
    have to use "var" and make it explicit.

    John Nagle

    --
  • Kevin Gillette at Jan 21, 2013 at 11:43 pm
    I agree with John Nagle's sentiments that := is tricky and troublesome at times, particularly with respect for it's double as both assignment and declaration.

    A bit of friendliness can be extended to the user by always considering the blank identifier undeclared, so that the following would work:

    package main

    import "fmt"

    func main() {
    x, y, _ := 1, 2, 3
    x, y, _ := 4, 5, 6
    fmt.Println(x, y)
    }

    This would lend more utility to _ for quick hacking.

    I think ideally (but it's too late for it now), := would be better either silently accepting redecls in the same scope (falling back to assignment, even if there's nothing new on the lhs), or never declaring variables defined in outer scopes (though ignoring the existence of package and universe scope identifiers for this check), so that existing identifiers are assigned to, and unknowns are declared. For consistency, var could have mandated := use instead of =, and in the above latter case, as suggested by others, var could be the sole means of shadowing.

    I believe the current semantics are clear and reasonable for experienced gophers, but agree that it's non-obvious for newcomers.

    I like go's mechanical approach to shadowing, such that the outer scope variable is visible until the very point that it is shadowed by the inner decl. It should be expected to throw off programmers coming from python (and probably other langs), however, which has the opposite lookup semantics: for shadowing purposes, decls are hoisted to the top of the block, so attempting to refer to an outer variable before it is shadowed in that same block will result in a parse error along the lines of ”variable used before it is declared”.

    --
  • Nate Finch at Jan 22, 2013 at 12:24 am
    *Whenever* I use := in anything other than the outermost scope of the
    function, I always look carefully at the variables on the left side and
    double check that I'm not accidentally shadowing something I don't want to
    shadow.

    It's funny, I've had a lot more problems with "a is not declared" because I
    used = instead of := than accidentally shadowing by doing vice versa.
    On Monday, January 21, 2013 6:43:50 PM UTC-5, Kevin Gillette wrote:

    I agree with John Nagle's sentiments that := is tricky and troublesome at
    times, particularly with respect for it's double as both assignment and
    declaration.

    A bit of friendliness can be extended to the user by always considering
    the blank identifier undeclared, so that the following would work:

    package main

    import "fmt"

    func main() {
    x, y, _ := 1, 2, 3
    x, y, _ := 4, 5, 6
    fmt.Println(x, y)
    }

    This would lend more utility to _ for quick hacking.

    I think ideally (but it's too late for it now), := would be better either
    silently accepting redecls in the same scope (falling back to assignment,
    even if there's nothing new on the lhs), or never declaring variables
    defined in outer scopes (though ignoring the existence of package and
    universe scope identifiers for this check), so that existing identifiers
    are assigned to, and unknowns are declared. For consistency, var could have
    mandated := use instead of =, and in the above latter case, as suggested by
    others, var could be the sole means of shadowing.

    I believe the current semantics are clear and reasonable for experienced
    gophers, but agree that it's non-obvious for newcomers.

    I like go's mechanical approach to shadowing, such that the outer scope
    variable is visible until the very point that it is shadowed by the inner
    decl. It should be expected to throw off programmers coming from python
    (and probably other langs), however, which has the opposite lookup
    semantics: for shadowing purposes, decls are hoisted to the top of the
    block, so attempting to refer to an outer variable before it is shadowed in
    that same block will result in a parse error along the lines of ”variable
    used before it is declared”.
    --
  • Danielvallsestella at Jan 22, 2013 at 12:46 am
    I think it's not a good behaviour that

    var a int
    ...
    a, b := 5, 3

    COMPILES, but the same code removing b

    var a int
    ...
    a := 5

    DOESN'T COMPILES




    On Tuesday, January 22, 2013 12:43:50 AM UTC+1, Kevin Gillette wrote:

    I agree with John Nagle's sentiments that := is tricky and troublesome at
    times, particularly with respect for it's double as both assignment and
    declaration.

    A bit of friendliness can be extended to the user by always considering
    the blank identifier undeclared, so that the following would work:

    package main

    import "fmt"

    func main() {
    x, y, _ := 1, 2, 3
    x, y, _ := 4, 5, 6
    fmt.Println(x, y)
    }

    This would lend more utility to _ for quick hacking.

    I think ideally (but it's too late for it now), := would be better either
    silently accepting redecls in the same scope (falling back to assignment,
    even if there's nothing new on the lhs), or never declaring variables
    defined in outer scopes (though ignoring the existence of package and
    universe scope identifiers for this check), so that existing identifiers
    are assigned to, and unknowns are declared. For consistency, var could have
    mandated := use instead of =, and in the above latter case, as suggested by
    others, var could be the sole means of shadowing.

    I believe the current semantics are clear and reasonable for experienced
    gophers, but agree that it's non-obvious for newcomers.

    I like go's mechanical approach to shadowing, such that the outer scope
    variable is visible until the very point that it is shadowed by the inner
    decl. It should be expected to throw off programmers coming from python
    (and probably other langs), however, which has the opposite lookup
    semantics: for shadowing purposes, decls are hoisted to the top of the
    block, so attempting to refer to an outer variable before it is shadowed in
    that same block will result in a parse error along the lines of ”variable
    used before it is declared”.
    --
  • Andrew Gerrand at Jan 22, 2013 at 1:08 am
    Think about how awful error handling would be without this property.

    Andrew
    On 22 Jan 2013 11:46, wrote:

    I think it's not a good behaviour that

    var a int
    ...
    a, b := 5, 3

    COMPILES, but the same code removing b

    var a int
    ...
    a := 5

    DOESN'T COMPILES




    On Tuesday, January 22, 2013 12:43:50 AM UTC+1, Kevin Gillette wrote:

    I agree with John Nagle's sentiments that := is tricky and troublesome at
    times, particularly with respect for it's double as both assignment and
    declaration.

    A bit of friendliness can be extended to the user by always considering
    the blank identifier undeclared, so that the following would work:

    package main

    import "fmt"

    func main() {
    x, y, _ := 1, 2, 3
    x, y, _ := 4, 5, 6
    fmt.Println(x, y)
    }

    This would lend more utility to _ for quick hacking.

    I think ideally (but it's too late for it now), := would be better either
    silently accepting redecls in the same scope (falling back to assignment,
    even if there's nothing new on the lhs), or never declaring variables
    defined in outer scopes (though ignoring the existence of package and
    universe scope identifiers for this check), so that existing identifiers
    are assigned to, and unknowns are declared. For consistency, var could have
    mandated := use instead of =, and in the above latter case, as suggested by
    others, var could be the sole means of shadowing.

    I believe the current semantics are clear and reasonable for experienced
    gophers, but agree that it's non-obvious for newcomers.

    I like go's mechanical approach to shadowing, such that the outer scope
    variable is visible until the very point that it is shadowed by the inner
    decl. It should be expected to throw off programmers coming from python
    (and probably other langs), however, which has the opposite lookup
    semantics: for shadowing purposes, decls are hoisted to the top of the
    block, so attempting to refer to an outer variable before it is shadowed in
    that same block will result in a parse error along the lines of ”variable
    used before it is declared”.
    --

    --
  • Dave Cheney at Jan 22, 2013 at 1:11 am
    Folks,

    This mail thread is now approaching 40 replies and the tone of it has
    at times boarded on personal. Can I ask that everyone please take a
    few steps back from the keyboard for a day or so.

    Rest assured this issue will still be here when you get back, as it is
    an immutable part of the Go 1 syntax. Some distance may help to defuse
    the distressingly agressive tone of some of the replies.

    Thank you

    Dave
    On Tue, Jan 22, 2013 at 12:08 PM, Andrew Gerrand wrote:
    Think about how awful error handling would be without this property.

    Andrew
    On 22 Jan 2013 11:46, wrote:

    I think it's not a good behaviour that

    var a int
    ...
    a, b := 5, 3

    COMPILES, but the same code removing b

    var a int
    ...
    a := 5

    DOESN'T COMPILES




    On Tuesday, January 22, 2013 12:43:50 AM UTC+1, Kevin Gillette wrote:

    I agree with John Nagle's sentiments that := is tricky and troublesome at
    times, particularly with respect for it's double as both assignment and
    declaration.

    A bit of friendliness can be extended to the user by always considering
    the blank identifier undeclared, so that the following would work:

    package main

    import "fmt"

    func main() {
    x, y, _ := 1, 2, 3
    x, y, _ := 4, 5, 6
    fmt.Println(x, y)
    }

    This would lend more utility to _ for quick hacking.

    I think ideally (but it's too late for it now), := would be better either
    silently accepting redecls in the same scope (falling back to assignment,
    even if there's nothing new on the lhs), or never declaring variables
    defined in outer scopes (though ignoring the existence of package and
    universe scope identifiers for this check), so that existing identifiers are
    assigned to, and unknowns are declared. For consistency, var could have
    mandated := use instead of =, and in the above latter case, as suggested by
    others, var could be the sole means of shadowing.

    I believe the current semantics are clear and reasonable for experienced
    gophers, but agree that it's non-obvious for newcomers.

    I like go's mechanical approach to shadowing, such that the outer scope
    variable is visible until the very point that it is shadowed by the inner
    decl. It should be expected to throw off programmers coming from python (and
    probably other langs), however, which has the opposite lookup semantics: for
    shadowing purposes, decls are hoisted to the top of the block, so attempting
    to refer to an outer variable before it is shadowed in that same block will
    result in a parse error along the lines of ”variable used before it is
    declared”.
    --
    --
    --
  • Rui Maciel at Jan 21, 2013 at 7:52 pm

    On 01/21/2013 03:02 PM, ziutek wrote:
    I am happy with ':=' properties (I'm programing in Go since 2010). I
    only suggest that would be nice if go vet warn me when sees such code:

    sl := pmt.ESInfo()
    for len(sl) > 0 {
    i, sl := sl.Pop()
    // ...
    }

    because I'm not a machine, so I make such mistakes sometimes.
    You don't need to be a machine. The following is taken out of Go's
    specification for declarations and scope[1]:

    <quote>
    An identifier declared in a block may be redeclared in an inner block.
    While the identifier of the inner declaration is in scope, it denotes
    the entity declared by the inner declaration.
    </quote>

    The ':=' thing is called "short variable declaration", which gives
    enough emphasis to the fact that, by using it, a new variable is either
    declared or redeclared.

    So, if ':=' declares a new variable and with Go a variable can be
    redeclared within an inner block, your example does work as expected.
    If, instead, there was an attempt to redeclare a variable within the
    same block, that wouldn't be valid:

    http://play.golang.org/p/vSUCj4h55c

    Instead of requiring a compiler flag, it would be better, and safer, to
    simply assume that every short variable declaration either declares a
    variable or redeclares it. Because that's essentially what actually
    happens.


    Rui Maciel

    [1] http://golang.org/ref/spec#Declarations_and_scope

    --
  • Ziutek at Jan 21, 2013 at 10:04 pm

    On 21 Sty, 20:52, Rui Maciel wrote:
    You don't need to be a machine. The following is taken out of Go's
    specification for declarations and scope[1]:

    <quote>
    An identifier declared in a block may be redeclared in an inner block.
    While the identifier of the inner declaration is in scope, it denotes
    the entity declared by the inner declaration.
    </quote>

    The ':=' thing is called "short variable declaration", which gives
    enough emphasis to the fact that, by using it, a new variable is either
    declared or redeclared.

    So, if ':=' declares a new variable and with Go a variable can be
    redeclared within an inner block, your example does work as expected.
    If, instead, there was an attempt to redeclare a variable within the
    same block, that wouldn't be valid:

    http://play.golang.org/p/vSUCj4h55c

    Instead of requiring a compiler flag, it would be better, and safer, to
    simply assume that every short variable declaration either declares a
    variable or redeclares it.  Because that's essentially what actually
    happens.

    Rui Maciel

    [1]http://golang.org/ref/spec#Declarations_and_scope
    First: Thank you (and other folks) for trying explaining me how ':='
    works. Funny, I known very well how ':=' works but even than I
    accidentally make some mistakes. Of course, I believe that this only
    happens to me and those who do not know how ':=' works. It's nice to
    know that there are some people (not machines) who don't make
    programming mistakes because they known language specification.

    Second: I didn't suggest any new compiler flag, I suggest that it
    would be nice to have a tool (eg: extend go vet) to find such cases.

    --
  • Itmitica at Jan 21, 2013 at 10:49 pm

    On Tuesday, January 22, 2013 12:04:36 AM UTC+2, ziutek wrote:

    Second: I didn't suggest any new compiler flag, I suggest that it
    would be nice to have a tool (eg: extend go vet) to find such cases.
    Using the same token (i) on both sides of an initialization, that's either
    a decision or a slight logic error.

    I was suggesting that a tool may not be of much use in this particular
    case, which I believe has little to do with shadowing.

    --
  • Dan Kortschak at Jan 21, 2013 at 11:14 pm
    There is knowing and there is always getting it right. I know how :=
    works, but I've still been bitten with extremely embarrassing bugs
    resulting from shadowing like this.
    On Mon, 2013-01-21 at 14:04 -0800, ziutek wrote:
    Of course, I believe that this only
    happens to me and those who do not know how ':=' works.
    --
  • Patrick Mylund Nielsen at Jan 21, 2013 at 8:09 pm
    Totally agree.

    On Mon, Jan 21, 2013 at 10:02 AM, ziutek wrote:
    On 21 Sty, 15:45, Martin Angers wrote:
    Le lundi 21 janvier 2013 09:24:21 UTC-5, mic...@zeto.wroc.pl a écrit :


    To me this is actually expected. Lexical scoping, as others mentioned. Your
    "i" gets redefined in the enclosing block. In the "for" it is always
    initialized with the value of the outer "i" minus one. It can be confusing
    if you come from, say, javascript, which only has function-level scope, but
    for C-like languages with block scoping, this is pretty standard.
    I am happy with ':=' properties (I'm programing in Go since 2010). I
    only suggest that would be nice if go vet warn me when sees such code:

    sl := pmt.ESInfo()
    for len(sl) > 0 {
    i, sl := sl.Pop()
    // ...
    }

    because I'm not a machine, so I make such mistakes sometimes.

    --

    --
  • Itmitica at Jan 21, 2013 at 9:05 pm

    On Monday, January 21, 2013 5:02:54 PM UTC+2, ziutek wrote:

    I am happy with ':=' properties (I'm programing in Go since 2010). I
    only suggest that would be nice if go vet warn me when sees such code:

    sl := pmt.ESInfo()
    for len(sl) > 0 {
    i, sl := sl.Pop()
    // ...
    }

    because I'm not a machine, so I make such mistakes sometimes.
    Forgive me if I overstep, I've only started Go last autumn, but writing i,
    sl := sl.Pop() and expecting sound results is the same with writing var a
    int = a + 1 and expecting valid results also.

    Left side and right side do have different meaning. sl.Pop() right side
    evaluation is the first simple concern for that assignment, long before any
    shadowing concerns be considered.

    If I'm wrong, I apologize in advance.
    Mitică

    --
  • Chris dollin at Jan 21, 2013 at 2:34 pm

    On 21 January 2013 12:37, ziutek wrote:
    I found a bug in my code, caused by ":=", which was very hard to find.
    This is simplified example:

    http://play.golang.org/p/nxthpeYv3p

    In my real code Iter was a slice and Pop removes one element from it:

    func (i Iter) Pop() (int, Iter) {
    return i, i[1:]
    }

    The problem is because the intention was to get k and return updated
    slice (don't change i), but compiled code gets k and redeclares i.

    The ':=' properties cause that this code:

    i := Iter(10)
    for n := 0; n < 3; n++ {
    k, i := i.Pop()
    fmt.Println(k, i)
    }

    isn't expanded to:

    var i Iter
    i = 10
    for n := 0; n < 3; n++ {
    var (
    i Iter
    k int
    )
    k, i = i.Pop()
    fmt.Println(k, i)
    }
    Treating "expands to" as "has the same meaning as", it looks
    to me as that's exactly what it "expands to". Inside the for loop
    you declare two new variables called `k` and `i` which are initialised
    from the expression i.Pop() which refers to the outer `i` -- `k` and
    the inneer `i` don't come into scope until after the initialising expressions.
    which in my case will be easier to debug, but it expands to

    var i Iter = 10
    for n := 0; n < 3; n++ {
    var (
    k Iter
    tmpI int
    )
    k, tmpI = i.Pop()
    var i = tmpI
    fmt.Println(k, i)
    }

    which in real word sometime works sometime not.
    That looks like it would mean the same thing.

    Chris

    --
    Chris "allusive" Dollin

    --
  • Dan Kortschak at Jan 21, 2013 at 7:19 pm
    It can't mean this. when you declare i in the loop, what is the value it takes? Zero. So how would this implementation behave? Certainly not how you expect with i having an initial value of 10. And as with the other case, this will true for each iteration.

    This problem is classic shadowing.
    On 22/01/2013, at 1:04 AM, "chris dollin" wrote:


    isn't expanded to:

    var i Iter
    i = 10
    for n := 0; n < 3; n++ {
    var (
    i Iter
    k int
    )
    k, i = i.Pop()
    fmt.Println(k, i)
    }
    Treating "expands to" as "has the same meaning as", it looks
    to me as that's exactly what it "expands to". Inside the for loop
    you declare two new variables called `k` and `i` which are initialised
    from the expression i.Pop() which refers to the outer `i` -- `k` and
    the inneer `i` don't come into scope until after the initialising expressions.
    --
  • Chris dollin at Jan 21, 2013 at 8:33 pm

    On 21 January 2013 19:19, Dan Kortschak wrote:
    It can't mean this. when you declare i in the loop, what is the value it takes? Zero. So how would this implementation behave? Certainly not how you expect with i having an initial value of 10. And as with the other case, this will true for each iteration.

    This problem is classic shadowing.
    On 22/01/2013, at 1:04 AM, "chris dollin" wrote:


    isn't expanded to:

    var i Iter
    i = 10
    for n := 0; n < 3; n++ {
    var (
    i Iter
    k int
    )
    k, i = i.Pop()
    fmt.Println(k, i)
    }
    Treating "expands to" as "has the same meaning as", it looks
    to me as that's exactly what it "expands to". Inside the for loop
    you declare two new variables called `k` and `i` which are initialised
    from the expression i.Pop() which refers to the outer `i` -- `k` and
    the inneer `i` don't come into scope until after the initialising expressions.
    Bleagh, I quietly missed that the i in i.Pop was now the wrong i. I should
    have stuck to my "there's no 'expands to' here" approach.

    Thanks for catching it.

    Chris

    --
    Chris "... not the swcope you're looking for." Dollin

    --
  • Dan Kortschak at Jan 21, 2013 at 8:38 pm
    I think that's good argument for either a module in vet or a 'go shadow' tool.
    On 22/01/2013, at 7:03 AM, "chris dollin" wrote:

    Bleagh, I quietly missed that the i in i.Pop was now the wrong i. I should
    have stuck to my "there's no 'expands to' here" approach.

    Thanks for catching it.

    Chris
    --
  • Itmitica at Jan 21, 2013 at 9:45 pm

    On Monday, January 21, 2013 2:37:01 PM UTC+2, ziutek wrote:
    I found a bug in my code, caused by ":=", which was very hard to find.
    The problem is because the intention was to get k and return updated
    slice (don't change i), but compiled code gets k and redeclares i.
    Another thing I want to add.
    As far as I can tell, It's not about shadowing, it's about programming
    logic.

    go vet is just a tool, it can't really check the programming logic for me,
    that's my job, I'm the programmer.
    In all respects, you'd never consider a code like this:
    http://play.golang.org/p/K2t8QFFj6P

    --
  • Patrick Mylund Nielsen at Jan 21, 2013 at 9:50 pm
    The same can be said about forgetting to terminate a string in C, etc.

    You can write bad code in any language, but some languages make it harder.
    Go is one of those. In some areas it falls a bit short of that. I don't
    know of a language that doesn't. In those cases you can assist users
    instead of just saying "RTFM" and "it's clearly defined." (golang.org does
    have a FAQ page, after all...)

    On Mon, Jan 21, 2013 at 4:45 PM, wrote:
    On Monday, January 21, 2013 2:37:01 PM UTC+2, ziutek wrote:

    I found a bug in my code, caused by ":=", which was very hard to find.
    The problem is because the intention was to get k and return updated
    slice (don't change i), but compiled code gets k and redeclares i.
    Another thing I want to add.
    As far as I can tell, It's not about shadowing, it's about programming
    logic.

    go vet is just a tool, it can't really check the programming logic for
    me, that's my job, I'm the programmer.
    In all respects, you'd never consider a code like this:
    http://play.golang.org/p/K2t8QFFj6P

    --

    --

Related Discussions

People

Translate

site design / logo © 2021 Grokbase