Good article. It seems fair and well-thought-out. I do disagree with some of his criticisms of the language, specifically:
> interfaces are always references
How does one expect a dynamic-dispatch interface to be implemented in a static language if not by reference? This is the most common case, and it's how dynamic-dispatch is implemented in every language with which I'm well acquainted.
> comparing interfaces to nil is allowed by the compiler, but apparently not a good idea, and can lead to some strange bugs
This was strange to me at first, but when you realize that an interface is a reference, it makes sense that an interface can be nil. If you don't know that interfaces are reference types and your interface is implemented by a pointer, you might think that `if myInterfaceVar == nil` is testing the nullity of the underlying pointer when it's really testing the nullity of the interface. Because Go programmers are expected to know that interfaces are reference types (just like maps, pointers, slices, etc), it's perfectly idiomatic to test them against nil.
> channels without size are blocking
Channels are a synchronization mechanism, so it seems natural that they would block when full. What other behavior makes sense? Grow infinitely, possibly until the program runs out of memory? Write over unread values?
This isn't to say that the author isn't allowed to find these details surprising or unintuitive; only that the more "intuitive" thing isn't obviously easy and/or it might not be universally intuitive.
> interfaces are always references
How does one expect a dynamic-dispatch interface to be implemented in a static language if not by reference? This is the most common case, and it's how dynamic-dispatch is implemented in every language with which I'm well acquainted.
> comparing interfaces to nil is allowed by the compiler, but apparently not a good idea, and can lead to some strange bugs
This was strange to me at first, but when you realize that an interface is a reference, it makes sense that an interface can be nil. If you don't know that interfaces are reference types and your interface is implemented by a pointer, you might think that `if myInterfaceVar == nil` is testing the nullity of the underlying pointer when it's really testing the nullity of the interface. Because Go programmers are expected to know that interfaces are reference types (just like maps, pointers, slices, etc), it's perfectly idiomatic to test them against nil.
> channels without size are blocking
Channels are a synchronization mechanism, so it seems natural that they would block when full. What other behavior makes sense? Grow infinitely, possibly until the program runs out of memory? Write over unread values?
This isn't to say that the author isn't allowed to find these details surprising or unintuitive; only that the more "intuitive" thing isn't obviously easy and/or it might not be universally intuitive.