4 ms·
> Because Go gives us interfaces and closures we can write much more elegant, generic APIs with a flavor similar to Ruby or Lisp and this is the direction the l
by oconnor663 11y ago
> Because Go gives us interfaces and closures we can write much more elegant, generic APIs with a flavor similar to Ruby or Lisp and this is the direction the language naturally wants us to take. Personally I like to use the empty interface for plumbing and only pin things down to specific interfaces or concrete types where I need to for performance or correctness.
That's a lot like using opaque pointers in C. What is it about Go that makes people assume it's shortcomings are beautiful designs?
I learned yesterday that `x == nil` can return false even if x is nil so long as x is an interface type. But it depends on whether x is actually nil or a nil value with a specific type.
(╯°□°)╯︵ ┻━┻
My other pet peeve is that a method with a non-pointer receiver that tries to modify the receiver object will silently drop those modifications on the ground, because the object is copied. Which makes some sense, except that Go likes to convert to pointer receivers automatically, so the caller can't tell that anything is wrong. The only difference is one character in the method definition. Everyone I know hits this bug at some point and loses half an hour before they learn to look for it. You could almost say "all method receivers must be pointers" except that you need to refer to interface types without the pointer.
(╯°□°)╯︵ ┻━┻
- Strom 11y agoEverything in Go is passed by value (i.e. copied), even the pointers. The sooner you will understand this, the less problems you will have.
- oconnor663 11y agoThe part that confuses me is that a lot of pointers are taken implicitly. Method receivers depend on the method definition, and maps (and interfaces?) are always pointers. So it can be hard for me to tell the difference between pass-by-value and pass-pointer-by-value.
- falcolas 11y agoThe confusing thing is that those copies are wrapped in interface wrapper types when passed into a function which uses interfaces in its function signature.
- jerf 11y agoThat's true of nearly every language. "Passed by reference" is almost non-existant in reality, there's only "Pass by value copy" and "pass by copy of the reference" in most cases. The nature of structured programming and the way parameters come in to functions all but forces this; if you have a real function call (that is, not inlined), the reference has to be a copy. There's a few interesting exceptions to this, but they're very much exceptions.
- eternalban 11y agoGo's simplicity is merely syntactical. The semantics can be subtle and not entirely aligned with intuition honed after x years of working with C-like language X. This is an observation and not a critique. > I learned yesterday that `x == nil` can return false even if x is nil so long as ... In other words: http://play.golang.org/p/YV5ylbQN4j http://play.golang.org/p/YV5ylbQN4j Thanks for the heads-up! That was news to me.
- tptacek 11y agoWhen does it make sense to compare a string with nil?
- eternalban 11y agoIt doesn't but the function admits a T. It actually makes perfect sense, it is just a bit subtle.
- oconnor663 11y agoIn the playground you have there, the string `v` is implicitly initialized to the empty string, which is not equal to nil. I'm referring to a different condition, where two different values are explicitly set to nil. Check this out: https://play.golang.org/p/qMmg7yOAKO https://play.golang.org/p/qMmg7yOAKO
- infogulch 11y agoYes this is what trips many people up. One reason `(* T)(nil) != nil` is because a `(* T)(nil)` can still have pointer methods called on it, and it can still work fine (as long as the receiver is never dereferenced, of course): https://play.golang.org/p/rup5cqapEU https://play.golang.org/p/rup5cqapEU
- falcolas 11y agoActually, the issue mentioned by the OP is covered here: http://golang.org/doc/faq#nil_error http://golang.org/doc/faq#nil_error Basically, if you pass 'nil' of one type into an argument which accepts an interface of another type, the nil is no longer nil, but an interface wrapper around a nil (which is not nil). So: var buf *bufio.Reader // a nil value if this is passed into a function with a signature of func foo(buf io.Reader) then when in function foo: foo(buf) // valid, and buf is nil here //inside foo: if buf != nil { // but buf is not nil here, so this is true buf.Read() // Panics because nil has no function Read.
- danieldk 11y agoThat's called using opaque pointers. It's pretty much what you do in C. ??? The empty interface is effectively dynamic typing, relying on runtime introspection. It does not hide any struct members (unless you make them private), you can simply enumerate them. Opaque pointers in C provide a way to enforce static typing without exposing struct members and are effectively a method for information hiding. So, I am wondering why you said that. My other pet peeve is that a method with a non-pointer receiver that tries to modify the receiver object will silently drop those modifications on the ground, because the object is copied. I agree. In fact, it is seldom that I actually want the non-pointer variant. If I don't modify the object, just give me a const modifier so that I can indicate that. (Or reasoned from outside Go: the receiver should IMO always be passed by reference.)
- oconnor663 11y agoYou're right. I just toned down my language. What I meant to say is that doing away with type safety is an old idea, and its downsides are old too. You end up with code that's harder to read, because you can't tell what the types are supposed to be, and you run into bugs that show up far away from your mistakes.
- danieldk 11y agoI agree. Just wanted to check if I missed something :) (which happens often enough ;)).