←back to thread

Go is still not good

(blog.habets.se)
644 points ustad | 2 comments | | HN request time: 0s | source
Show context
827a ◴[] No.44986330[source]
Recently I was in a meeting where we were considering adopting Go more widely for our backend services, but a couple of the architect level guys brought up the two-types-of-nil issue and ultimately shot it down. I feel like they were being a little dramatic about it, but it is startling to me that its 2025 and the team still has not fixed it. If the only thing you value in language design is never breaking existing code, even if by any definition that existing code is already broken, eventually the only thing using your language will be existing code.
replies(3): >>44986569 #>>44986646 #>>44990658 #
dilap ◴[] No.44986569[source]
This has already been explained many times, but it's so much fun I'll do it again. :-)

So: The way Go presents it is confusing, but this behavior makes sense, is correct, will never be changed, and is undoubtedly depended on by correct programs.

The confusing thing for people use to C++ or C# or Java or Python or most other languages is that in Go nil is a perfectly valid pointer receiver for a method to have. The method resolution lookup happens statically at compile time, and as long as the method doesn't try to deref the pointer, all good.

It still works if you assign to an interface.

  package main
  
  import "fmt"
  
  type Dog struct {}
  type Cat struct {}
  
  type Animal interface {
   MakeNoise()
  }
  
  func (*Dog) MakeNoise() { fmt.Println("bark") }
  func (*Cat) MakeNoise() { fmt.Println("meow") }
  
  func main() {
   var d *Dog = nil
   var c *Cat = nil
   var i Animal = d
   var j Animal = c
   d.MakeNoise()
   c.MakeNoise()
   i.MakeNoise()
   j.MakeNoise()
  }
This will print

  bark
  meow
  bark
  meow
But the interface method lookup can't happen at compile time. So the interface value is actually a pair -- the pointer to the type, and the instance value. The type is not nil, hence the interface value is something like (&Cat,nil) and (&Dog,nil) in each case, which is not the interface zero value, which is (nil, nil).

But it's super confusing because Go type cooerces a nil struct value to a non-nil (&type, nil) interface value. There's probably some naming or syntax way to make this clearer.

But the behavior is completely reasonable.

replies(3): >>44986596 #>>44986842 #>>44990252 #
1. 827a ◴[] No.44986842[source]
I deeply, seriously, believe that you should have written the words "Its super confusing", meditated on that for a minute, then left it at that. It is super confusing. That's it. Nothing else matters. I understand why it is the way it is. I'm not stupid. As you said: Its super confusing, which is relevant when you're picking languages other people at your company (interns, juniors) have to write in.

> “The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt.”

replies(1): >>44987265 #
2. dilap ◴[] No.44987265[source]
It's a sharp edge you trip over once, and makes sense once you think about it, it's not like you need a PhD to understand it!

I do think there probably would've been some more elegant syntax or naming convention to make it less confusing.