3 ms·
Enums: yes, absolutely, good call Universal Nil: no. we need less nil not more. Nil-safe types would be a better answer. Error-checking: If you're not going t
by marcus_holmes 5y ago
Enums: yes, absolutely, good call
Universal Nil: no. we need less nil not more. Nil-safe types would be a better answer.
Error-checking: If you're not going to do anything with the error, then have an editor snippet for the ? boilerplate. Better still, start handling the error. The boilerplate is a Go anti-pattern.
I think we do need more clarity about the relationship between slices and arrays: it's not clear at the moment when two slices share a backing array or not, and therefore whether changes to one slice will change the other.
I'm kinda looking forward to generics, but also dreading it. Most newbie gophers don't understand how powerful duck-typed interfaces are and what they can do with them, and I have a feeling generics will be used for all sorts of situations that could be better handled with interfaces.
- Joker_vD 5y agoIt's not universal nil, it's more universal zero. This proposal, in one way or another, ― using "_", or "{}", or "nil" ― has been around since 2016 at least, because zero-initializing a struct is indeed way too verbose. > Better still, start handling the error. Most of the time, you can do this only two-three levels higher on the call stack, and even here it's most likely to be wrapping up of "return nil, fmt.Errorf("frobbing bar: %w", err)" sort. And yes, I am too excited and scared of the generics. I'd like to see generic map- and channel-related algorithms and patterns, and sort.Interface is an ugly hack, even if an ingenious one.
- marcus_holmes 5y ago> because zero-initializing a struct is indeed way too verbose hmm. I don't have the problem you're describing, because I generally declare the return struct at the start of the function and then return that. E.g. func something() (mystruct,error){ result := mystruct{} if badthing() then { return result, fmt.Errorf("bad thing happened: %w",err) } //do something to result before returning it return result } I must admit I don't understand how the proposal works fully. Would `x==nil` return true for an int with value 0? Both answers to this seem wrong to me.
- JulianMorrison 5y agonil for non-pointer types should only be assignable, not comparable.
- marcus_holmes 5y agoIt doesn't solve the problem then, because for non-pointer types we have to have multiple zero values: one for assignment and one for comparison.
- Joker_vD 5y agoTo be fair, it's not that much of a problem, and even then: "if result == _ { ... }" instead of "if result == MyPrettyStruct{} { ... }" is arguably more intent-revealing, as for "if number == _ { ... }", well, I guess the recommendation would be "just don't do that". Perhaps gofmt could even automatically replace it?
- simiones 5y ago> hmm. I don't have the problem you're describing, because I generally declare the return struct at the start of the function and then return that. The problem with this is that, in a larger function, I have to read all the way up to confirm if result is an empty value or something else. I think returning myStruct{} is much better, and having a way to easily return a default myStruct would be nice. Calling that "nil" seems to be a bad idea to me, as others are pointing out. > I must admit I don't understand how the proposal works fully. Would `x==nil` return true for an int with value 0? Both answers to this seem wrong to me. I think it would return false in that case. There is already precedent for this in Go, in fact, with arrays/slices. Even though slices themselves are value types (they wrap a pointer) they can be nil, but []someStruct{} == nil is false.
- marcus_holmes 5y agoYeah but that's because the zero value for slices is an empty slice, not a nil slice. This is what we should have more of. If in the proposal (x==nil) == false then we get the ridiculous position where: var x int x=nil if x==nil { //this never happens despite having just set x=nil } which seems ridiculous. Though the other case, of returning true, also seems ridiculous, because nil and 0 are not the same thing at all.
- lenkite 5y agoThe extraordinarily repetitive error handling in Go drives me batty and I am thankfully not the only one who feels this way Part One: Endless Error Handling https://jesseduffield.com/Gos-Shortcomings-1/ https://jesseduffield.com/Gos-Shortcomings-1/ That blog also covers a lot of Go's wonderful defects advertised as 'features'.
- JyB 5y agoFYI https://news.ycombinator.com/item?id=28522269 https://news.ycombinator.com/item?id=28522269
- marcus_holmes 5y agoWhereas it doesn't bother me at all. I've used Go for years now and it feels totally normal to check the error condition after every call. It feels strange switching to JS and there's just nothing - stuff can just error at any time and we're not going to do anything about it? That feels odd ;)
- lenkite 5y agoYou can - one can put an exception handler at the place one truly wishes to handle the error as opposed to always introducing repetitive code at every single place in the call chain. The latter tends to make the eyes glaze over in tedium after some time and due to the signal/noise ratio one misses real bugs in the error handling.
- marcus_holmes 5y agoThe number of JS scripts I've had to add any error handling to is too damn high. I like that Go forces at least some kind of error handling. The "signal/noise ratio" problem fades after a while - you start seeing "if err != nil {...}" as a single statement after a while, a steady rhythm in the code. You notice when it's not there, or when it's doing something different.
- rattlesnakedave 5y ago>You can - one can put an exception handler at the place one truly wishes to handle the error as opposed to always introducing repetitive code at every single place in the call chain. I don't understand this point but I hear it raised often. At best I'm lukewarm on Go's error handling. Having if err != nil feels the same to me ergonomically as a try / catch. Am I missing something by thinking that?
- JyB 5y ago> I'm kinda looking forward to generics, but also dreading it. Most newbie gophers don't understand how powerful duck-typed interfaces are and what they can do with them, and I have a feeling generics will be used for all sorts of situations that could be better handled with interfaces. That really is the scariest part about it. I really hope the Go community will take this issue to hearth and push back against codebases with unnecessary generics. Otherwise the whole ecosystem will suffer from it.
- jwalton 5y ago> Most newbie gophers don't understand how powerful duck-typed interfaces are Duck-typed interfaces are kind of broken in Go, though. I can use an Impl anywhere I can use an Abstract, but if I have a function that takes a slice of Abstract, I can't pass it a slice of Impl, because it's the wrong type.
- marcus_holmes 5y agoSorry I don't really understand your terminology?
- jwalton 5y agotype Abstract interface { value() string } type Impl struct { str string } func (impl Impl) value() string { return impl.str } func ShowAbstracts(arr []Abstract) { for _, a := range arr { fmt.Println(a.value()) } } func main() { arr := []Impl{Impl{"a"}, Impl{"b"}} ShowAbstracts(arr) // This doesn't work, because arr is []Impl, not []Abstract } It works with `arr := []Abstract{Impl{"a"}, ...}` of course, but if you already have a 100K slice of Impl, you'd have to copy all 100K elements into a slice of Abstract to call ShowAbstracts in this example.
- marcus_holmes 5y agoThanks for the explanation :) Slices are backed by arrays, not actual arrays - copying a 100K slice into another slice isn't as intensive as it sounds. I'm not sure I see this as "interfaces are broken in Go", or something that requires generics to solve? (I do realise that the language itself uses generics to implement the copy keyword for this).
- jwalton 5y agoUnfortunately I think copying 100K elements is going to be exactly as painful as it sounds. The `[]Impl` is going to be (more or less) an array of 100K strings. The `[]Abstract` will be an array of interfaces, each of which is internally two pointers, so I'll have to allocate 1.6M of RAM and fill that RAM with pointers in order to convert a `[]Impl` into a `[]Abstract`. Generics fix this handily, because I can write: func ShowAbstracts[T Abstract](arr []T) { for _, a := range arr { fmt.Println(a.value()) } } And now when I try to pass in my `[]Impl`, Go will compile a new `ShowAbstracts(arr []Impl)` transparently behind the scenes.
- zohch 5y ago> Universal Nil: no. we need less nil not more. Nil-safe types would be a better answer. Everyone needs optional values, due to Go's poor design the closest to this is to use nil.
- throwaway894345 5y agoI strongly agree with this. I'll also note that "enums" solves for nil (universal or otherwise). > I have a feeling generics will be used for all sorts of situations that could be better handled with interfaces. Very much agree. I'll also extend "interfaces" to include closures.