On Wed, May 30, 2012 at 9:06 PM, Jesse McNelis <
jes...@jessta.id.au> wrote:
> On Thu, May 31, 2012 at 7:03 AM, Aaron Bohannon <
aaro...@gmail.com> wrote:
>> I guess my confusion results from the fact that, in Go, assigning an
>> expression (e.g., the expression "nil") to a variable or passing it to
>> a function can change its dynamic type, whereas in most languages
>> assignment can only change an expression's static type.
>
> I think the implicit converstion to interface is confusing you.
> Conversion to interface is the only implicit conversion that Go does.
>
> var a int = 0
> var b interface{} = a
>
> is actually:
>
> var a int = 0
> var b interface{} = interface{}(a)
>
> So when you pass something to a functions that takes an interface{}
> the value you pass in is converted to an interface{} value
Yeah, that's part of it. Basically, it boils down to this:
Everything in the language is designed to make it appear as if assignment to an
interface variable is creating an "is a" relationship....e.g., I can write:
var x int = 3
var a interface{} = x
fmt.Println(a == 3) // prints "true"
switch (a) {
case 3: fmt.Println("true")
default: fmt.Println("false")
} // "true" is printed
But it's actually creating a "has a" relationship. And the _only_ reason I need
to be aware, as a programmer, that it's creating a "has a" relationship is
because of the way the expression "nil" is handled The "nil" of the interface
type appears to "shadow" the "nil" of a reference type:
var x *int = nil
var a interface{} = x
fmt.Println(a == nil) // prints "false"
switch (a) {
case nil: fmt.Println("true")
default: fmt.Println("false")
} // "false" is printed
So, because of "nil", I need to be aware that a "has a" relationship is being
created when I assign something to an interface variable. Except....that's not
always true! It's not true when the thing I'm assigning to the interface is
another sort of interface type. Then the assignment really _is_ creating an "is
a" relationship:
type Foo interface{}
var x Foo = nil
var a interface{} = x
fmt.Println(a == nil) // prints "true"
switch (a) {
case nil: fmt.Println("true")
default: fmt.Println("false")
} // "true" is printed
Rationally, I know exactly what's going on in all these examples and how it's
implemented, etc, etc. But there's a part of my brain that will just never be
comfortable with it.
Since these "has a" relationships can only go one level deep, the "shadowing" of
nil has the appearance of being an unfortunate coincidence rather than the
outworking of some master plan. That is, if the language had simply used "null"
to refer to the zero value of reference types (other than interfaces) and "nil"
to refer to the zero value of interfaces, then there would be no opportunity for
this "shadowing" problem to happen. And, of course, I can hear people now,
saying "Two sorts of nil? That would be way too confusing! It's much cleaner
to just have one." But it's not cleaner! It's not simpler. Because now
programmers have to think about "is a" vs. "has a" relationships, implicit
conversion, untyped constants, etc, etc. What would have been an perfect,
elegant illusion is shattered by one detail that would have been trivial to
design differently.
Honestly, I don't know why it bothers me. Or why it took me so long to sort out
what I was confused about. Obviously, it doesn't bother everybody. And it
probably doesn't confuse everybody. But I don't know if it's because the flaw
in the illusion is so insignificant in most people's eyes that they just don't
notice it, or if it's because most people don't even care about programming in
languages that provide illusions and would rather just think about the guts of
what's going on all the time. (I have exactly the same question about C++
programmers, but Go is a dream-world in comparison with C++, so it's quite a
different question in that context.
- Aaron