Live data from Hacker News

Having fun with Go's nil, interfaces and errors

katcipis.github.io

21–30 of 52 posts

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

#21
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 Go, it's called "zero value", not "nil value".

It's also not an implicit conversion from nil to something. Nil isn't a value the way 0 is a value of an integer or "foo" is a value of string.

Per Go spec (https://golang.org/ref/spec) nil is "predeclared identifier which has no type". That implies it doesn't have a value and therefore cannot be converted to a value.

Nil is zero value of pointer types (which in practice end up being a pointer whose value interpreted as integer is 0) and also a zero value of interface type (which is interface without a type and without a value) and a few other types.

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

#22

Earlier quoted context omitted.

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 le…

The behavior of Go's nil does not make the language any simpler to implement in any meaningful sense. The only thing it affects at the machine level is which cmp instructions are generated.

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

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

Most likely the reason is keeping the language implementation simple. Java-style `null` is actually quite subtle: http://stackoverflow.com/a/2707333/46571

Go has the same complexity. From the Go spec: "For an expression x of interface type and a type T, the primary expression x.(T) asserts that x is not nil and that the value stored in x is of type T.'"

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

#24
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…

Java and C# also have interfaces and they don't have this strange behavior around null. I don't see how it's any sort of natural consequence of having null plus interfaces. Rather I suspect the reason is that the type equality operator was implemented to check one word and not the other when comparing two-word interface types, and the behavior that resulted was eventually specified instead of changing the implementation to compare both words.

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

#25

Earlier quoted context omitted.

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 le…

> It's a stunningly difficult and surprisingly low-value proposition to create abstractions that don't leak.

Maybe for you. For me, non-leaky abstractions have enormous value. And, while designing non-leaky abstractions requires more effort upfront, in the long run, it requires less effort than plugging leaks in carelessly designed (non)abstractions.

> That's not the goal anyway: The goal is to leverage abstractions to build things.

The goal is to do it efficiently, and creating as few problems as possible for the future.

> The implementation details frequently do matter.

Of course they do matter - for the implementor.

> When possible, it's preferable to design abstractions that have a clear implementation in terms of the lower level's abstractions.

No disagreement here.

> This way, when something goes wrong - and it will - you can understand and fix it.

That's the job of the abstraction's implementor. If that happens to be me, I will fix it. Otherwise I'll just submit a bug report. If that doesn't work, then I'll reimplement the abstraction myself, but by no means will I work around an existing broken implementation.

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

#26
post #18

Earlier quoted context omitted.

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

In this case, Java and C# have the same feature and don't have this behavior around null. I think it's reasonable for people who have seen identical features in other languages to expect similar behavior when learning new ones.

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

#27
> This is a very interesting situation (and not a very common one for me) because it is where Go’s simple errors as values decisions shines. Doing this kind of thing with exceptions would be pretty clumsy, [...]

Just because a language supports exceptions does not mean you always have to use them. You can always use a helper function to catch any error and return it as a value.

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

#28
post #18

Earlier quoted context omitted.

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…

In this case, Java and C# have the same feature and don't have this behavior around null. I think it's reasonable for people who have seen identical features in other languages to expect similar behavior when learning new ones.

They don't have the same feature of "I can call a method on a null value", at least, not that I've seen... You are calling them identical features when the fact is that they aren't. C# and Java both have generics, do they perform identical? Haskell has generics too, but I bet they'd throw a shit-fit if you tried to say "Oh, C# has generics too, so why would I bother with Haskell?"
Post reply on HN