7 ms·
I seem to be a bit of an outlier, but I am not looking forward to generics in Go. I don't like the kinds of discussions it attracts...vague hand-wavy arguments
by cle 5y ago
I seem to be a bit of an outlier, but I am not looking forward to generics in Go. I don't like the kinds of discussions it attracts...vague hand-wavy arguments about "expressiveness" and "purity". Expressiveness to me means more arguing in code reviews, and nothing is stopping us from writing pure functions now, I do it all the time.
I maintain a few large Go codebases, tens of thousands of LoC each, and not having generics in practice is way, way down on my list of challenges. Equivalent Java codebases that I've maintained are usually harder to maintain because of generics, with over-eager functional programmers itching to show off their skills in abstraction, instead of solving business problems.
I hope I'm wrong, and that this will make Go a better language. But I doubt it.
- klodolph 5y agoI'm hoping people won't go crazy with it. Generics are one of the things that drive me nuts about the Rust ecosystem... it feels like every library in Cargo is based around a bunch of types with a bunch of unnecessary type parameters. Like, if I want to parse command-line arguments. What I do want is to stop implementing sort.Interface over and over again.
- cle 5y agoAgree, this is my hope. Seeing the same thing play out in other language ecosystems though, I'm just not very optimistic about it.
- masklinn 5y ago> Like, if I want to parse command-line arguments. What command-line parsing libraries have “a bunch of unnecessary type parameters”? Only library I can think of which even has them would be structopt, and that’s not unnecessary, the entire point of the library is to deserialise to a structured type. And of course rust was designed from the ground up to leverage generics, e.g. by design it does not have reflection / RTTI, interactions with variable or foreign types are thus generics based (often, sometimes trait objects will do though they have their own tradeoffs), or it doesn’t have overloading so while not handing all overloading use cases by a long shot it uses conversion traits (aka generics) for “convenience” overloads e.g. foo(string) and foo(path) become foo<T>(_: T) where T: AsRef<Path>.
- klodolph 5y agoYes, Rust was designed from the ground-up to leverage generics, and as a result, the ecosystem is full of generics. There is overuse of generics in Rust. > ...by design it does not have reflection / RTTI,.. I don't think of those as comparable features. Reflection is more or less an alternative way to accomplish many of the same things that you get with macros in Rust. Where you'd see reflection in C# or Go is for something like serializing an object to JSON, and in Rust you'd get some library with a macro to do it for you. The Rust version could still be completely monomorphic, most of the time. RTTI is just std::any. Rust has it. However, 99% of the time, when I'm using dynamic_cast<T>(x) in C++ or switch x := x.(type) in Go, I would have been using an enum in Rust. The convenience overloads for Rust are nice, that's not the overuse I'm talking about. I'm talking about the fact that some library authors seem to avoid &dyn like it were poisonous, even if it makes total sense and would make large chunks of your library monomorphic. There are also minor code size issues that come about because some library authors forget that the convenience overload should probably just call an underlying monomorphic function, especially for cases like AsRef arguments. I am under the assumption here that if you really like generics, you probably like Rust, and vice versa. This is kind of a taste thing, so what is overuse of generics for me (hello, C++'s std::chrono, std::mt19937) might be the bee's knees for you. I did a fair bit of Haskell and C++ before Rust, and as I got more experience with those languages, I used generics less. Monomorphic types are always easier to understand, and they should be preferred.
- masklinn 5y ago> There is overuse of generics in Rust. You made that claim several time, but still provided literally no evidence, just a lot of handwaving and careful avoidance of any sort of concrete point. > I don't think of those as comparable features. > Reflection is more or less an alternative way to accomplish many of the same things that you get with macros in Rust. They go together e.g. where Go uses reflection, Rust uses Derive or manually implemented traits and generic functions working with those traits. JSON is a pretty obvious example there. > I'm talking about the fact that some library authors seem to avoid &dyn like it were poisonous, even if it makes total sense and would make large chunks of your library monomorphic. I would not necessarily disagree with that although I would word it more kindly: since generics always work, they're the default, as a result dynamic dispatch is a fallback when either you must have dynamic dispatch or someone pointed out that the static dispatch was less than optimal (which can be impossible to find out through microbenchmarks). Plus `&dyn` means you're now restricted to borrow-ability, and `Box<dyn>` incurs allocation costs. Generic function skip both concerns.
- duped 5y agoThe vast majority of the usage of generics in Rust are as replacements for inheritance or dynamic dispatch. It's not very different structurally.
- judofyr 5y agoAs someone who's working on code base at ~100k LoC I'd say that generics would be great to have: - We have some utilities (e.g. caching layer) which now either needs to use interface{} (and lose all type safety) or hard-code the type their dealing with (and thus be duplicated). - Lack of iterators are annoying. We often need to stream data from a database and now we need to introduce a separate iterator-interface for each thing that can be streamed (`Next() (Entry, error)`). Working with these iterators are clunky since we can't write common tooling around it (e.g. "fetch the first entry").
- cle 5y agoI've written quite a few containers that use interface{} like that...they don't really "lose all type safety", you write a typed container around it (usually a one line implementation) that does the type assertion in one spot, and that's it, you never deal with interface{} again. For all practical purposes, it's just as type safe. I agree that lack of iterators is one area where there is permanent annoyance. Personally, I think that annoyance is a lot less than the other problems that come with generics (at least in other languages). I'm not saying there aren't legitimate scenarios where generics simplify things. I'm completely convinced that there are. But holistically I don't think they outweigh all the other baggage that they come with.
- deleted 5y ago[deleted]
- moldavi 5y agoI've often wondered about this. Does this technique (a type safe wrapper around an interface{}ing class) kind of accomplish the same thing that generics do for Java? It would result in the same kind of type-erasure in the end, I'd assume. In a weird way, with this technique, did Go have "manual generics" all along?
- pharmakom 5y agoExcept you still have to write out the typed implementation - even if it is small. In Java this is not necessary.
- jshen 5y agoI have similar feelings. I’ve been coding a long time, and the longer I do it, the less sure I am about any of this stuff. The one thing I can say for sure, I find it relatively easy to jump into an arbitrary go code base and make sense of it. I can’t say this about languages with all the features people are clamoring for, and I’ve worked on large projects in Ruby, python, Java, scala, clojure, swift, JavaScript, typescript, and a few others. I’m not sure exactly which qualities/features of Go make it so much easier to read, but I worry that change will lead to the Go losing what makes it unique and special. Side note: clojure was oddly the second easiest for me to read.
- edem 5y agoIt is no surprise that Clojure was the easiest to read. It is composed of S expressions and some syntactic sugar.
- kaba0 5y agoMaking sense of a small, detailed part of a codebase is meaningless. But too much detail will make it much harder to see the big picture, something that may be more apparent in more expressive languages. On a deliberate hyperbole, reading assembly, each line is trivial, but it is very very hard to make sense of what does it do compared to a line of a high level language.
- gher-shyu3i 5y ago> I’m not sure exactly which qualities/features of Go make it so much easier to read, In my experience, golang is harder to read. It's overly verbose that it's hard to figure out the underlying logic. What can be written as items.filter(|n| n.price >= 10) .groupBy(|n| n.source) .mapValues(|v| v.sum()/v.length()) would be over a dozen lines of code in golang, with potentially one off functions everywhere. This only gets more obnoxious with larger code bases. The problem is that even if golang added generics, they won't be composable. In their proposal, they proposed adding map/filter/etc functions in a separate package, not as functions on slices as a proper language would do. The same mistake they did with not having any functions on strings, but kept them in a separate package. Also, they need to improve their anonymous function syntax. foo("param1", func(a int, b string) bool { return len(b) < a } is verbose, more advanced languages simply shorten it to foo("param1", |a, b| b.length() < a) or something equivalent
- sudhirj 5y agoThere was a thought expressed in one of the original drafts that went along the lines of "the only people worrying about generics will be library authors", which I think will and should be true. The structure and architecture of existing codebases won't change, but the removal of casting to and from interface{} when using libraries will be a welcome change.
- eru 5y ago> [...] and nothing is stopping us from writing pure functions now, I do it all the time. One point of generics is that you have to write your pure functions less often..
- whateveracct 5y ago> I don't like the kinds of discussions it attracts...vague hand-wavy arguments about "expressiveness" and "purity". Pretty much every argument about how to write Go programs is hand-wavy. I'd actually argue (from years of experience at least) that FP discussions of code are pretty much the only ones of actual substance instead of mostly aesthetics and opinion and heuristic. The best part of writing Haskell professionally is I no longer have to deal with hand-wavy programming folk-isms. We can do better, and it's not that hard.
- cle 5y agoAny stylistic discussions are like that, I agree. The opinions that Go makes are themselves hand-wavy, like any stylistic opinion. What's important to me is that the decision is already made, and when me and my team are writing code, we don't have to worry about making those decisions over and over again. They're already made for us. IME the costs/benefits of the different styles are not even worth the discussion, just pick something and move on. This is the aesthetic that I like about Go.
- jackcviers3 5y agoExactly. Is it RT? Is it well-typed? Is it as simple as it could be? Is there a small number of inhabitants? Ship it. Using some weird recursion scheme when fold right will do?
- haolez 5y agoAll languages are converging to the ultimate language: The TypePyGon! Jokes aside, I wish our tools were more "final". When is a programming language done regarding the pain it was meant to solve?
- Zababa 5y ago> When is a programming language done regarding the pain it was meant to solve? Elixir is often presented as "done" (https://elixirforum.com/t/is-elixir-done/20830/12 https://elixirforum.com/t/is-elixir-done/20830/12). For example, the depreciation policy mention that function can only be removed on a major version (1.X to 2.Y for example), and for now it's still in 1.X and no Elixir 2 is planned. It still has releases (https://elixir-lang.org/blog/2021/05/19/elixir-v1-12-0-released/ https://elixir-lang.org/blog/2021/05/19/elixir-v1-12-0-relea...) to follow Erlang releases, but Erlang itself is also more conservative than some other languages.
- munificent 5y ago> When is a programming language done regarding the pain it was meant to solve? It's hard to imagine that a language can be "done" when the problems being solved with it keep changing and we keep discovering new techniques for solving them. When is automotive design done? Farming?
- goatlover 5y agoThen why not just use C++ since it’s already accumulated all the features?
- munificent 5y agoThe absence of features is often one of the greatest values a tool can have. Features that you don't use carry a cognitive load, increase the number of choices you have to make, and reduce the usability of other features that they have to be disambiguated with.
- agumonkey 5y agomaybe go culture and community will add its own pragmatic flavour to generics use ? not everything has to end up in endless categorical soup
- bfrog 5y agoI hit the 100kloc size project in Go, the lack of generics and the never ending nested if err != nil lines were a huge part of that. It wore on myself and my team, and was certainly not helpful for the rsi I was experiencing. 10kloc is pretty small these days realistically. Especially in Go land so I get it. But yeah from my experience that simplicity at some point becomes more a burden and less a help.
- marcus_holmes 5y agoI'm totally with you on this.
- somezero 5y agoIt depends on the type of the work you do. Whenever I have to cast ints to floats because I want to use something in math library, I want to claw my eyes out.
- flippinburgers 5y agoMost problems that programmers are trying to "solve" seem rather banal to me. They aren't inherently difficult in a way that generics really does much to help. Honestly I think abstraction is often the route that smart - or wanting to be perceived as smart - programmers take to keep themselves entertained. Ultimately it is overkill and wastes time in my opinion.
- Fire-Dragon-DoL 5y agoGenerics can and will be abused for terrible stuff, that's a reality. There are however a bunch of spots where the difference in not having them is a lot of additional manual code, or unoptimized code using reflection. Every language feature can be abused, that's why Go tries to have a small amount