8 ms·
Generic interfaces
- tapirl 1y ago> This idea proves to be surprisingly powerful when it comes to expressing constraints on generic functions and types. Disagree. IMHO, this idea is the root cause of why Go generics is so complicate but also restrictive at the same time. And it introduces significant challenges in implementation and design: https://go101.org/generics/888-the-status-quo-of-go-custom-generics.html https://go101.org/generics/888-the-status-quo-of-go-custom-g...
- dlock17 1y agoI didn't realize how important order was to type inference. Are there any real packages out there using these techniques?
- Merovius 1y ago> I didn't realize how important order was to type inference. I was unclear, I'm afraid. You can reorder the type parameters, it just changes which of them you need to specify: https://go.dev/play/p/oDIFl3fZiPl https://go.dev/play/p/oDIFl3fZiPl The point is that you can only leave off elements from the end of the list, to have them automatically inferred. > Are there any real packages out there using these techniques? I think so far, the usage of generics for containers in Go is still relatively sparse, in public code. I think in part that is because the documentation of how to do that is relatively sparse. That is part of the motivation for the post, to have a bit of somewhat official documentation for these things, so they become more widely known. The standard library is just starting to add generic containers: https://github.com/golang/go/issues/69559 https://github.com/golang/go/issues/69559 And part of that is discussing how we want to do things like this: https://github.com/golang/go/issues/70471 https://github.com/golang/go/issues/70471 That being said, I have used the pointer receiver thing in my dayjob. One example is protobuf. We have a generic helper to set a protobuf enum from the environment. Because of how the API was designed, that required a pointer receiver constraint.
- dlock17 1y agoThe automatic part was what I was referring to, yes. I didn't realize you wrote the article, thanks! The article mentions using the function version to implement all others, but also that the method version would be optimized better. Would the compiler be able to inline MethodTree's compare even though it's passed in as a function variable to node.insert?
- Merovius 1y agoIn practice, currently, that depends on inlining decisions. If the function taking the function (say `node.insert`) is inlined, then yes. There are also other optimizations, like escape analysis, that matter here: the compiler can prove that the arguments to `node.insert` only escape into the `cmp` passed in. That decision is kept as metadata on `node.insert` even if it is not inlined. So if you pass a method expression to it, it can actually look at that and decide that the arguments don't escape from it either and hence that they don't escape overall. Whereas if you pass a `func` field, it can make no assumptions. My larger point though, is that with the `func` field the compiler can't optimize things even in principle. A user could always reassign this field (if nothing else using `*t = *new(FuncTree)`). So the compiler has to treat it as a dynamic call categorically. If the `func` is passed as a function, then at least in principle, it can prove that this function can't get modified during the call so can make optimization decisions based on what is being passed to it. For example, even without inlining, a future compiler might decide to compile two versions of `node.insert`, one with general dynamic calls and one specific one for a specific static function. My philosophy when it comes to API decisions that impact performance is, not to make them too dependent on what the compiler is doing today, but just to take care there is enough information there, that the compiler can do an optimization in principle - which means it either will do it today, or we can make it smarter in the future, if it becomes a problem.
- cyberax 1y agoLike many other people, I tried my hand at a generic container library. It worked, but was surprisingly impractical. For example, debugging was hell - there are no custom type renderers in delve.
- asim 1y agoIf I'm being honest, the magic of Go was lost when generics were introduced. It now feels akin to Java, which I guess was inevitable and for anyone to really take it seriously maybe it needed to get here. But I am not a fan of generics. While that level of abstraction and composability is clever, it also lends itself to more complexity and systems that can be harder to concretely understand. Just an opinion that I know many will not agree with but I come from the systems side rather than pure software engineering. It's probably ironic considering go-micro leans heavily on interfaces for abstraction but in that there are many hard learned lessons.
- iamkoch 1y agoInteresting perspective. Coming from C#, whose generics are first class, I struggled to obtain any real value from Go's generics. It's not possible to execute on ideas that fit nicely in your head, and you instead end up fighting tooth and nail to wrangle what feels like an afterthought into something concrete that fits in your head. Generics works well as a replacement for liberally using interface{} everywhere, making programs more readable, but as class and interface level I tend to avoid it as I find I don't really understand what is going on. I just needed it to work so I could move on
- bob1029 1y agoNo one is forcing you to use the full scope of language features for every project. This kind of argument comes up every time a new C# language version rolls out - as if it's a breaking change and now everyone is going to be forced to refactor for it. The only other way I can read this is in terms of wishing others would use tools in the way you prefer, which is clearly a waste of energy.
- bravesoul2 1y agoYeah but imagine a horrible feature. Let's say a macro language in brain fuck. Sure don't use it. But then half the libraries you use force it upon you. You soon have no choice!
- Cthulhu_ 1y ago
- tucnak 1y agoHorrorshow. Longterm Go user and open source maintainer here.. looking at this code makes me want to puke. The whole thing is a crime against semantics. I thought the whole point was to do generics when they could do them well, right? This is unwell..
- Traubenfuchs 1y ago“Maybe generics like in java and C# actually made sense after all.“ Welcome to civilization, golang. Were there ever any language developers with more hybris?
- exceptione 1y agoI think of it less as hybris and more of a (failed) experiment. It was deliberately built as a 'stupid' language for fresh undergrads, lacking design experience. It is a technical solution for a people problem. It is better to guide and to mentor people in designing the right abstractions. What we should learn from this experiment is that this is the wrong approach.
- guappa 1y agoTo me it's very useful. Whenever someone tells them go is their favourite language I know they can't be trusted.
- eru 1y ago> It is a technical solution for a people problem. It is better to guide and to mentor people in designing the right abstractions. What we should learn from this experiment is that this is the wrong approach. Nah, it was just the wrong solution. People problems are basically intractable in the grand scheme of things. Whenever you can turn a people problem into a technical problem, that's an opportunity for progress. Imagine telling everyone to be a professional and being careful not to break our program when they edit the code? Sounds like a big people problem! Instead, we give everyone their own copy to muck around with (instead of a shared folder), and we only allow changes to be integrated into the 'master copy', if they pass automated tests. A good manager and really motivated and professional workers can help cope with people problems. But there's a limit to their ability. So the more we can offload to technological solutions, the more 'professionalism' (for lack of a better word) we can spare for other task that aren't feasible to be solved via technology, yet. And I agree that not all technical solutions work! You need to experiment, and make judgement calls.
- 1y ago
- abtinf 1y ago> At this point, you might feel pretty overwhelmed. This is rather complicated and it seems unreasonable to expect every Go programmer to understand what is going on in this function signature. We also had to introduce yet more names into our API. When people cautioned against adding generics to Go in the first place, this is one of the things they were worried about. One of the key benefits of Go, at least for me, was not having to think about any of this at all ever. Whenever I touch generics, I find myself engrossed in the possibility of cleverly implementing something. Hours will pass as I try to solve the fun puzzle of how to do the thing using generics, rather than just solve the problem at hand.
- DanielHB 1y agoIt exchanges it for code-generation pain. Which one is worse is on a case-by-case basis. I imagine that people who prefer code-generation just like the idea of it having a higher skill/investment floor to add it to a project so most projects instinctively avoid it. While people who prefer generics jump at it even when it is not necessary or doesn't bring a lot of benefits. But those are human problems, not so much shortcomings of those two techniques themselves.
- lenkite 1y agoI find C++ templates simpler than Go generics. With C++, you can at-least get to a design solution. With Go generics: oops this is not possible, oops that is not possible - all because of strange language limitations. Go's Generics are a crippled implementation - they don't really deserve the feature title of 'generics'. (Its like saying you support regex, but don't support groups and repeat operators and you can only match them to special types of strings.)
- Merovius 1y agoI don't disagree that Go's generics are pretty limited. But I find it a strange complaint, when contrasted with C++ templates. Which, as I understand, are literally not part of the type system and thus there seems to be a far stronger case, that they can not be called generics. The main difference between Go's generics and C++ templates (and where some of the restrictions come from) is that Go insists that you can type-check both the body of a generic function and the call to it, without one having to know about the other. My understanding is, that with C++ templates (even including concepts), the type checking can only happen at the call-site, because you need to know the actual type arguments used, regardless of what the constraints might say. And this decision leads to most of the complaints I've heard about C++ generics. The long compile times, the verbose error messages and the hard to debug type-errors. So, if you prefer C++ templates, that's fair enough. But the limitations are there to address complaints many other people had about C++ templates specifically. And that seems a reasonable decision to me, as well.
- eru 1y agoC++ templates are duck typed at compile time. Look at Haskell type classes or Rust's traits for some classic examples of how to 'type' your generics. (And compare to what Go and C++ are doing.)
- Merovius 1y ago> C++ templates are duck typed at compile time. "Compile time" is not the right distinction. This is about "instantiation time". Go's implementation specifically allows to type-check the body and the call separately. That is, if you import a third-party package and call a generic function, all the type checker needs to look at to prove correctness is the signature of the function. It can ignore the body. This is especially relevant, if you call a generic function from a generic function. For C++, proving that such a call is correct is, in general, NP-complete (it directly maps to the SAT problem, you need to prove that every solution to one arbitrary boolean formula satisfies a different boolean formula). So the designers made the conscious decision to just not do that, instead delaying that check to the point at which the concrete type used to instantiate the generic function is known (because checking that a specific assignment satisfies a boolean formula is trivial). But that also means that you have to (recursively) type-check a generic function again and again for every type argument provided, which can drive up compilation time. A demonstration is this program, which makes gcc consume functionally infinite amount of memory and time: https://godbolt.org/z/crK89TW9G https://godbolt.org/z/crK89TW9G (clang is a little bit more clever, but can ultimately be defeated using a similar mechanism). Avoiding these problems is a specific cause for a lot of the limitations with Go's generics. > Look at Haskell type classes or Rust's traits for some classic examples of how to 'type' your generics. (And compare to what Go and C++ are doing.) Yes, those are a different beasts altogether and the differences between what Go is doing and what Haskell and Rust are doing requires different explanations. Though it's illustrative, because it turns out Rust also intentionally limited their generics implementation, to solve the kinds of performance problems Go is worried about. Specifically, Rust has the concept of "Dyn compatibility" (formerly "Object safety") which exists because otherwise Rusts goal of zero-cost abstractions would be broken. Haskell doesn't have this problem and will happily allow you to use the less efficient but more powerful types. (All of this should have the caveat that I'm not an expert in or even a user of any of these languages. It's half-knowledge and I might be wrong or things might have changed since I last looked)
- ISNIT 1y agoSort of wild that the Go blog doesn't have Go syntax highlighting...
- ntstr 1y agoIt makes more sense if you know about Rob Pike: https://groups.google.com/g/golang-nuts/c/hJHCAaiL0so/m/kG3BHV6QFfIJ?pli=1 https://groups.google.com/g/golang-nuts/c/hJHCAaiL0so/m/kG3B... >Syntax highlighting is juvenile. When I was a child, I was taught arithmetic using colored rods (http://en.wikipedia.org/wiki/Cuisenaire_rods http://en.wikipedia.org/wiki/Cuisenaire_rods). I grew up and today I use monochromatic numerals. The language creator really hates it (and most modern editor tooling).
- Cthulhu_ 1y agoIt's definitely weird in this day and age, but in the Go code examples... I don't miss it. Paraphrasing, but if you need syntax highlighting to comprehend code, maybe your code is too complicated.
- alkonaut 1y agoHow does that matter, if it's more _easily_ comprehended (faster, with less effort, with fewer mistakes in comprehension) with the highlighting, for any level of complexity? Not choosing to use syntax highlighting is just wrong on every level. It has exactly zero drawbacks.
- aranw 1y ago> if it's more _easily_ comprehended (faster, with less effort, with fewer mistakes in comprehension) with the highlighting But this is completely relevant to the person reading. It may be for you easier with highlighting but someone else it may not be
- alkonaut 1y agoYes. And there should be studies that show that the number of people who are hampered by syntax highlighting is probably so vanishingly small sompared to those that are either helped or not helped (unhelped, but not hampered) Syntax highlighting studies usually don't report on whether some subjects perform worse with syntax highlighting - usually only that they as a group perform better. But even with that evidence, it should be obvious that syntax highlighting should be either on for everyone, or on initially and off as an option for the rare individual. https://ppig.org/files/2015-PPIG-26th-Sarkar1.pdf https://ppig.org/files/2015-PPIG-26th-Sarkar1.pdf
- bravesoul2 1y agoWait till they hear about typeclasses!
- tapirl 1y agoIf Go generics support typeclasses, things will be much better now. At least custom generics and built-in generics will be unified harmoniously. Now, the manners of type argument passing with the built-in `new` and `make` function and custom generic functions are different. The inconsistency increases the load of cognition burden in Go programming. It is pity that Go generics designer never expressed the intention to unify custom generics and built-in generics.
- booleandilemma 1y agoWhen I first learned about Go I thought the idea was to have a simple C-like language with a frozen feature set. A language that would look the same today and ten years from now. And I thought that boringness was a wonderful feature, actually. If they're going to be adding features to the language, albeit at a slower pace than Java/C#, what's the point really? On a long enough timeline Go is going to be indistinguishable from these more feature-rich languages.
- eru 1y ago> When I first learned about Go I thought the idea was to have a simple C-like language with a frozen feature set. C is a C-like language with a mostly frozen feature set. (If you want something less insane than C, there's also Pascal.)
- vbezhenar 1y agoC is not frozen. C11 added generics, multi-threading, unicode support, static assertions. It broken compatibility with earlier versions by removing `gets` function. C23 added `nullptr`, very fundamental change. typeof operator. auto keyword for type inference. Lots of breaking changes by introducing new keywords. Another breaking change is empty brackets `()` now mean as function taking no arguments. So lots of new features and breaking changes with every new iteration. Thankfully, compilers support sane standards, so you can just use `-ansi` and live happy life, I guess...
- haiku2077 1y ago> A language that would look the same today and ten years from now. You can still write Go 1.11 code and compile it with the Go 1.24 compiler. If you specify 1.11 in your go.mod it compiles your code with the 1.11 syntax.
- ivanjermakov 1y agoRemember when Go proposed as a simple language? What a shitshow. Seems like Go's designers didn't know about interfaces, generics, and iterators when decided to make a language...
- vbezhenar 1y agoMarket for simple language opened. Someone will fill it. Go failed expectations, by turning into C++.
- tapirl 1y agoIt was also promoted as a language that prioritizes explicitness. But just look at the changes made in Go 1.22 (3-clause for-loop semantic change, [1]) and 1.23 (iterators, [2]). Magic implicitness was introduced in the two versions. Even worse, it was also promoted to keep backboard-compatibility seriously. But Go 1.22 broke the backward-compatibility so badly ([3] [1]). Despite this, the Go 1.22 release notes still claims "As always, the release maintains the Go 1 promise of compatibility". [1]: https://go101.org/blog/2024-03-01-for-loop-semantic-changes-in-go-1.22.html https://go101.org/blog/2024-03-01-for-loop-semantic-changes-... [2]: https://go101.org/blog/2025-03-15-some-facts-about-iterators.html https://go101.org/blog/2025-03-15-some-facts-about-iterators... [3]: https://go101.org/bugs/go-build-directive-not-work.html https://go101.org/bugs/go-build-directive-not-work.html And the change makers even have no interests to fix the problems caused by the changes: * https://github.com/golang/go/issues/66070#issuecomment-1981642904 https://github.com/golang/go/issues/66070#issuecomment-19816... * https://github.com/golang/go/issues/71830 https://github.com/golang/go/issues/71830 * https://github.com/spq/pkappa2/issues/238 https://github.com/spq/pkappa2/issues/238 * https://github.com/golang/go/issues/66388 https://github.com/golang/go/issues/66388 * https://github.com/golang/go/issues/71685 https://github.com/golang/go/issues/71685
- ricardobeat 1y agoPreaching to the choir here, but this is why a lot of the Go community was against generics. Especially in the era of AI assistants, the downside of writing out explicit types and repetition matters very little, while the upside of avoiding all this complexity is unmeasurable.
- gabrielgio 1y agoGeneric is not about reducing how many keys you press but how you abstract your logic from the type. In go, it reduces a lot code making it safer and faster. Handling interface{} was just painful. This is an extreme example and I hardly think anyone writing go code on a daily bases will need anything close to this. I haven't and I have not seen any lib that does anything remotely similar to that. To be honest, hardly anything beyond the stdlib will need to handle generics. They aren't widely used but quite useful when needed, which I think it is sweet-spot for generics. I don't share the same animosity against generics. I like the recent language addition to the stdlib and am also waiting for them to add some sugar to reduce the boilerplate in error handling. > Especially in the era of AI assistants, the downside of writing out explicit types and repetition matters very little Yeah, let's design languages based on the capabilities of code assistance /s
- syklemil 1y ago>> Especially in the era of AI assistants, the downside of writing out explicit types and repetition matters very little > Yeah, let's design languages based on the capabilities of code assistance /s I mean, that _is_ essentially the Go team's take these days, c.f. their previous blog post about error handling: https://go.dev/blog/error-syntax https://go.dev/blog/error-syntax > Writing repeated error checks can be tedious, but today’s IDEs provide powerful, even LLM-assisted code completion. Writing basic error checks is straightforward for these tools. The verbosity is most obvious when reading code, but tools might help here as well; for instance an IDE with a Go language setting could provide a toggle switch to hide error handling code. Personally I expect that getting an LLM to write error handling and then have the IDE hide it sounds like a recipe for surprises, but I guess things work out differently if the goal is to have hordes of the cheapest possible juniors kitted out with tools that let them produce the most amount of code per dollar.
- henry700 1y ago>There is an idea that is not obvious until you hear about it for the first time: as interfaces are types themselves, they too can have type parameters Not obvious???? Go language designers and programmers are living in another world
- kelseyfrog 1y agoHow is this better than rewriting containers for specific element types? Go was supposed to be simple and I can't understand any of this rubbish.
- Merovius 1y agoIt seems fairly clear to me, that it is preferable to import `rsc.io/omap` over having to implement a self-balancing binary search tree?