Live data from Hacker News

Having fun with Go's nil, interfaces and errors

katcipis.github.io

11–20 of 52 posts

Re: Having fun with Go's nil, interfaces and errors

#12
post #3

Earlier quoted context omitted.

Except it's not a language implementation detail, it's part of what the language is . You might as well ask why a user of another language needs to know what a class is, or how inheritance works. Too many people seem to be assuming "Oh, it's like void*, so it must act like that always!", but it's not.

> Except it's not a language implementation detail, it's part of what the language is. Okay, but what was the rationale for designing the language like that?

The last bit of information you need to understand that is that unlike some other languages you may be used to, "nil" is a perfectly valid value for a pointer to have. You can write methods like this:

    func (o *Object) ReturnSomething() int {
        if o == nil {
            return 0
        }
        return o.SomeOtherValue
    }
It is also therefore legal to return in an interface value a nil pointer to a struct of a particular kind, and therefore "an interface containing a nil pointer of a particular type" and "an interface containing nothing at all" are fundamentally different things that can not be collapsed together.

So as Vendan says, it is just part of the language. All languages have this sort of wart in them, where two or more perfectly sensible decisions interact to create something that isn't sensible at first glance.

(I'm still in favor of going back in time and changing Go to have non-nillable values, but that ship has certainly sailed.)

Re: Having fun with Go's nil, interfaces and errors

#13
post #4

I got bit by this the other day when returning a nil pointer to a struct as an error. It was incredibly frustrating when I finally figured out what was going on, and I'm curious if there's a good reason to distinguish between these 2 types of nil or if it was just an oversight/mistake during the language design process.

It's not two types of nil.

When a nil value is converted to an interface, it carries type information. When an interface carries type information, it isn't nil.

Re: Having fun with Go's nil, interfaces and errors

#15
post #9
post #3

Earlier quoted context omitted.

Except it's not a language implementation detail, it's part of what the language is . You might as well ask why a user of another language needs to know what a class is, or how inheritance works. Too many people seem to be assuming "Oh, it's like void*, so it must act like that always!", but it's not.

After using Go for a few years, I've learned (the hard way) that Go does not separate the concepts of implementation details and the official language specification. The official implementation is the language, and they're inseparable. This is why a lot of design decisions about the language make you say "wait, what? why does it need to be that way?" and the community's official answer is "because that's how it works…

In the end, you are programming a computer.

Re: Having fun with Go's nil, interfaces and errors

#16
post #15
post #9

Earlier quoted context omitted.

After using Go for a few years, I've learned (the hard way) that Go does not separate the concepts of implementation details and the official language specification. The official implementation is the language, and they're inseparable. This is why a lot of design decisions about the language make you say "wait, what? why does it need to be that way?" and the community's official answer is "because that's how it works…

In the end, you are programming a computer.

Yes, but the whole point to using abstractions (such as high-level languages) is to separate the context where implementation details are relevant (e.g., when writing a compiler), from the rest of the system, where implementation details don't matter (e.g., when using a compiler).

Re: Having fun with Go's nil, interfaces and errors

#17
post #12

Earlier quoted context omitted.

> Except it's not a language implementation detail, it's part of what the language is. Okay, but what was the rationale for designing the language like that?

The last bit of information you need to understand that is that unlike some other languages you may be used to, "nil" is a perfectly valid value for a pointer to have. You can write methods like this: func (o *Object) ReturnSomething() int { if o == nil { return 0 } return o.SomeOtherValue } It is also therefore legal to return in an interface value a nil pointer to a struct of a particular kind, and therefore "an in…

> unlike some other languages you may be used to,

The languages I'm used to don't even have `nil`.

> "nil" is a perfectly valid value for a pointer to have.

So, just like C and Java. I don't see the difference.

> You can write methods like this: (snippet)

Oh, now I get it: `nil` itself isn't a value. `nil` is syntactic sugar that expands into a nil value. (In other words, confusingly enough, `nil` and “nil value” are different things!) This is unlike Java, where `null` itself is a value.

It would be very helpful if people described things precisely.

> All languages have this sort of wart in them, where two or more perfectly sensible decisions interact to create something that isn't sensible at first glance.

This means that at least one of the two (or more) decisions was less sensible than it originally seemed. Good design decisions don't introduce warts into a system.

Re: Having fun with Go's nil, interfaces and errors

#18
post #12

Earlier quoted context omitted.

The last bit of information you need to understand that is that unlike some other languages you may be used to, "nil" is a perfectly valid value for a pointer to have. You can write methods like this: func (o *Object) ReturnSomething() int { if o == nil { return 0 } return o.SomeOtherValue } It is also therefore legal to return in an interface value a nil pointer to a struct of a particular kind, and therefore "an in…

> unlike some other languages you may be used to, The languages I'm used to don't even have `nil`. > "nil" is a perfectly valid value for a pointer to have. So, just like C and Java. I don't see the difference. > You can write methods like this: (snippet) Oh, now I get it: `nil` itself isn't a value. `nil` is syntactic sugar that expands into a nil value. (In other words, confusingly enough, `nil` and “nil value” are…

nil doesn't "expand", it's just what it is. You have to bear in mind that methods are roughly syntactic sugar from

  func (o *Object) ReturnSomething() int
to

  func  ReturnSomething(o *Object) int
In the first case, it may "seem" that a nil Object would be invalid, but in the second case it's obviously valid, and makes complete sense. It may be described as a "wart", but it makes complete sense to me. The issue is, in my opinion, that people drag their own expectations into programming languages as they learn them.

Re: Having fun with Go's nil, interfaces and errors

#19
post #8

Earlier quoted context omitted.

> Except it's not a language implementation detail, it's part of what the language is. Okay, but what was the rationale for designing the language like that?

Sometimes you try and make one feature work a certain way and that decision has unintended consequences. Most languages have surprises like this. In this case, it's an implicit conversion from "nil" to the "nil value" of a given type. This is convenient in many cases, but causes confusion in other cases.

> In this case, it's an implicit conversion from "nil" to the "nil value" of a given type.

Upvoted because of this, because it clarified my earlier confusion. That being said, I don't really agree with the rest.

Re: Having fun with Go's nil, interfaces and errors

#20
post #15

Earlier quoted context omitted.

In the end, you are programming a computer.

Yes, but the whole point to using abstractions (such as high-level languages) is to separate the context where implementation details are relevant (e.g., when writing a compiler), from the rest of the system, where implementation details don't matter (e.g., when using a compiler).

It's a stunningly difficult and surprisingly low-value proposition to create abstractions that don't leak. That's not the goal anyway: The goal is to leverage abstractions to build things. The implementation details frequently do matter. Even if your abstraction is airtight, it has to run on a real machine. When possible, it's preferable to design abstractions that have a clear implementation in terms of the lower level's abstractions. This way, when something goes wrong - and it will - you can understand and fix it.
Post reply on HN