non-name (node XDOT) on left side of :=

2,517 views
Skip to first unread message

Petar Maymounkov

unread,
Jan 8, 2010, 11:53:55 AM1/8/10
to golang-nuts
This is a language design question.

The following does not work:

struct S { x int }
func f() (int, int)

func main() {
s = &S{}
s.x, n := f() // error in subject line kicks here
}

You circumvent it by writing

n int
s.x, n = f()

But this is annoying, especially when you are using Go idiom like:

if s.x, ok := f(); !ok { ... }

and you want the "ok" to be a local variable.

Is there a fundamental reason why this cannot be improved so as to
make the first example above (with the error) to actually work?

--Petar

Kevin Ballard

unread,
Jan 8, 2010, 4:30:59 PM1/8/10
to Petar Maymounkov, golang-nuts
:= binds all names on the left side as a new variable. s.x is not a variable.

-Kevin Ballard

Petar Maymounkov

unread,
Jan 8, 2010, 4:34:47 PM1/8/10
to golang-nuts
You are explaining why this doesn't work.

I am asking: Is there a fundamental language design reason
why it couldn't be made to work.

Petar

> kball...@gmail.com

Kevin Ballard

unread,
Jan 8, 2010, 4:40:13 PM1/8/10
to Petar Maymounkov, golang-nuts
Because := binds a new variable on the left. Changing that to work with non-variables completely eliminates the entire purpose of :=, and turns it into just plain =.

-Kevin Ballard

Petar Maymounkov

unread,
Jan 8, 2010, 4:44:10 PM1/8/10
to golang-nuts
Note that you can write
x,y :=
where at least one of x or y is new variable.
So, already, := is intelligent to distinguish between existing and
new variables.

So, the mixture of new vs. existing is not the core problem.

I am simply asking, when the parser sees

s.x, y :=

and recognizes that s.x is a field and y is a new variable, why can't
it treat
s.x as a plain/existing variable for the purpose of this assignment.

P

> kball...@gmail.com

Kevin Ballard

unread,
Jan 8, 2010, 4:46:39 PM1/8/10
to Petar Maymounkov, golang-nuts
Yes, but if you say

x, y :=

it still basically re-creates both variables. This doesn't make any difference if you say something like

var x string;
x, y := ...

but it certainly makes a difference when the assignment statement happens inside a nested scope. I ran into this exact problem in one of my programs, where my error parameter was always coming back nil even when I was assigning it to the results of a function call, because I was using := in the assignment and the outer scope's error variable was therefore being ignored.

-Kevin Ballard

Petar Maymounkov

unread,
Jan 8, 2010, 4:57:51 PM1/8/10
to golang-nuts

On Jan 8, 4:46 pm, Kevin Ballard <kball...@gmail.com> wrote:
> Yes, but if you say
>
> x, y :=
>
> it still basically re-creates both variables.

(1) This is somewhat of an ambiguous statement to me (at least)
(2) I am guessing that by "re-create" you mean that it discards the
old variable, say x,
and creates a new one under the same name. After which, all references
to "x" refer
to the new variable. If this is what you mean, there is no problem.
This is an
implementation issue, not a language interpretation issue.

> This doesn't make any
> difference if you say something like
>
> var x string;
> x, y := ...
>
> but it certainly makes a difference when the assignment statement happens
> inside a nested scope. I ran into this exact problem in one of my programs,
> where my error parameter was always coming back nil even when I was
> assigning it to the results of a function call, because I was using := in
> the assignment and the outer scope's error variable was therefore being
> ignored.

This does not sound true. What you probably did is the following:

var err = 8
if err := f(); err == 0 {
...
}
fmt.Printf("%v\n",err)

In this example there are two different err variables. One outside the
if body
and one inside. While you are inside the body, you only see the inner
one.
Changes to "err" made inside the if body do not affect the outside
variable.

P

> kball...@gmail.com

Kevin Ballard

unread,
Jan 8, 2010, 5:09:10 PM1/8/10
to Petar Maymounkov, golang-nuts
On Fri, Jan 8, 2010 at 1:57 PM, Petar Maymounkov <pet...@gmail.com> wrote:

(1) This is somewhat of an ambiguous statement to me (at least)
(2) I am guessing that by "re-create" you mean that it discards the
old variable, say x,
and creates a new one under the same name. After which, all references
to "x" refer
to the new variable. If this is what you mean, there is no problem.
This is an
implementation issue, not a language interpretation issue.

I disagree. This is very much a language interpretation issue. It's establishing a new variable that shadows the old one.
 
> This doesn't make any
> difference if you say something like
>
> var x string;
> x, y := ...
>
> but it certainly makes a difference when the assignment statement happens
> inside a nested scope. I ran into this exact problem in one of my programs,
> where my error parameter was always coming back nil even when I was
> assigning it to the results of a function call, because I was using := in
> the assignment and the outer scope's error variable was therefore being
> ignored.

This does not sound true. What you probably did is the following:

var err = 8
if err := f(); err == 0 {
 ...
}
fmt.Printf("%v\n",err)

Nope. I did something more akin to

var err = 0;
for (...) {
    ok, err := os.SomeCall();
    ....
}
if err != 0 {
    ...
}

My original understanding was that := is used to bind new variables, but it can be used on old variables if there's at least one new variable in the lhs. But it quickly became apparent that it was defining a brand new err variable within the nested scope, and outside of the scope the old err variable was in use again (the one that's always 0). The only reason why := cannot be used with all old variables on the lhs is because it is most likely a programmer error, and the language is preventing you from accidentally shadowing your variables.

So yes, by definition, := defines a new variable for every identifer on the lhs. If you give it something that's not a valid variable identifier, it's an error.

-Kevin Ballard

Petar Maymounkov

unread,
Jan 8, 2010, 5:14:47 PM1/8/10
to golang-nuts
Ok, good. So we've resolved and agree on what it currently does.

Still, the question remains, can the compiler simply recognize
that s.x is an existing struct field and re-assign it?

As a matter of fact it seems like there is no problem:
An expression of the form s.x (that has a dot in it) can never
be a variable, so it is clearly recognizable as a field,
in which case the compiler can take a different action,
which is to re-assign the field value.

P

> kball...@gmail.com

Petar Maymounkov

unread,
Jan 8, 2010, 5:17:07 PM1/8/10
to golang-nuts
In fact, the very existence of the error message
"non-name (XDOT)" tell you that the compiler
recognizes a different entity, called XDOT.
And currently there is no meaning assigned to
this entity being in this expression. But one can
simply assign a meaning to it (and implement it).

P

Kevin Ballard

unread,
Jan 8, 2010, 5:17:55 PM1/8/10
to Petar Maymounkov, golang-nuts
Yes, in theory it could be changed to do that, but I'm still arguing that it would be wrong to do that. That would horribly muddy the semantics of :=, and introducing imprecise semantics is not a good way to avoid writing a simple variable declaration.

-Kevin Ballard

Petar Maymounkov

unread,
Jan 8, 2010, 5:20:28 PM1/8/10
to golang-nuts
This is why this is a design question, and it would be nice to hear
the opinion of one of the language experts on the list.

In my first post, I have given one reason why it makes sense.

P

> kball...@gmail.com

Russ Cox

unread,
Jan 8, 2010, 5:25:11 PM1/8/10
to Petar Maymounkov, golang-nuts
The XDOT is not actually relevant here.
An XDOT is a DOT that has (or has not, I forget)
gone through implicit dot insertion to account for
embedded struct fields.  The error should be using
Go syntax instead of exposing such details.

Originally := always declared a new variable
(it was exactly shorthand for a var declaration).
Then we made it not an error to "redeclare" a 
variable in the current block.  Your suggestion 
is the obvious next step, and there's no technical
reason it can't be done, but so far we haven't done it.
It's tough to know exactly where to draw the line,
and we're focused more on larger aspects of the
language and implementation at the moment.
I think it's something that would be worth coming
back to after we have more experience writing
Go programs.

Russ

roger peppe

unread,
Jan 9, 2010, 6:53:03 PM1/9/10
to r...@golang.org, Petar Maymounkov, golang-nuts
2010/1/8 Russ Cox <r...@golang.org>:

> Originally := always declared a new variable
> (it was exactly shorthand for a var declaration).
> Then we made it not an error to "redeclare" a
> variable in the current block.

i'm starting to wonder if this wasn't a step too far;
when i use this property, i sometimes think that
i'm creating fragile code - if i move that code
code into an inner block then the semantics change,
often without any obvious sign that they've done so.

recently, i've been declaring the new variable
anyway and just using =; that way it's obvious
to the reader of the code which variable is new
and which is being assigned to, and moving the
assignment around won't change the meaning.
i can't decide if this is really better.

however, with regard to the original question: i don't see why
not - at least it's obvious that you're not trying to
declare a new variable x.y or whatever.

Reply all
Reply to author
Forward
0 new messages