10 ms·
Golang Diaries: Generics
- svnpenn 4y ago> func (c *container>[S]) size() int { This is not valid code.
- jallasprit 4y agoThis is one of the first things I remarked about the Go generics implementation. It is not yet possible to use Generics on struct methods.
- fsdjkflsjfsoij 4y agoThe restriction of not introducing new generic types on methods has nothing to do with why that code doesn't compile. Remove the '>' in the method signature and it works fine.
- morelisp 4y agoNo, it's not valid because there's a trivial typo probably caused by bad HTMLification. Remove the > and it's fine.
- Strum355 4y agoThats a separate issue from the one being shown here. The syntax here is just invalid
- Philip-J-Fry 4y agoYou can use generics, you just can't introduce new type parameters in the method. You're free to use the type parameters defined on the type you're writing the method for.
- tapirl 4y agoIt is obviously a typo.
- timbray 4y agoYep, fixed, thanks.
- morelisp 4y agoTwo thoughts: 1) The failure of func (t *fishTank) fishCount() string { return fmt.Sprintf("How many fishies? %d!", t.size()) } to work has nothing to do with generics; methods are always "removed" from types defined this way. If you want you can cast `t` to a `*container[fish]` and call it, but this is also where I would ask why "extend" rather than compose - are you gaining anything by ensuring identical layouts? 2) The bigger issue, that interfaces cannot require their implementations to be comparable, is https://github.com/golang/go/issues/52614 https://github.com/golang/go/issues/52614 and linked tickets.
- hiptobecubic 4y agoBut why (1)? If a `fishTank` is a `container[fish]` what is the benefit of requiring it to be cast again?
- morelisp 4y agoWhen people say Go is not an OO language, this is what they mean. Concrete type declarations define storage, not is-a hierarchies. A fishTank is not a container[fish], any more or less than it is any other `{ string, []fish }`.
- sidlls 4y agoAnd yet OO-isms are replete throughout implementations in go. `interface{}` all the things is another of the worst things about go. It practically begs for engineers to engage in all the worst practices of OO-ism and unit test zealotry.
- throwaway894345 4y agointerface{} isn’t pervasive in Go, it doesn’t tend to get abused often in practice, it has nothing to do with OO at all, and it doesn’t lead to any particular ideology about unit testing. It seems like you are mistaken on all counts.
- 4y ago
- deeptote 4y agoGo is my main language but it keeps disappointing me in it's lack of implementation tuning detail. Generics are just a code generator that, given it's poor performance for things like functional programming, makes it surprisingly of little use. Go is a kitchen full of dull knives. It is good for layers of communication where you call other microservices to perform the heavy lifting, but not much else.
- Thaxll 4y agoGo is on part with Java and C# and sometime as fast as C++, it's plenty fast for most usecases.
- deeptote 4y agoAgain, just one engineer's opinion, but that's because most use cases for engineers are CRUD. If you don't have a CRUD problem, which is probably less than 10% of modern software engineering, Go falls on it's face.
- Thaxll 4y agoUber, Dropbox etc... rely heavily on Go and have much more complicated use case that your CRUD hello world.
- throwaway894345 4y agoYou have it exactly backwards. Go is a particularly poor language for CRUD use cases (CRUD doesn’t care much about performance but it is very generic). Go shines for most non-CRUD applications.
- fsdjkflsjfsoij 4y ago> given it's poor performance for things like functional programming, makes it surprisingly of little use. In most cases it doesn't have "poor" performance and if you're going for optimal performance you are almost never going to be using generic data structures anyways because there's almost always type specific optimizations that can be done. The intended use case is, and always has been, decently performing type safe data structures and Go generics are sufficient in most cases for that. Go is never going to be a functional programming language.
- cpurdy 4y agoAdding a type system to an existing language that was never intended to have that type system is not simple. And in Go, the language seems designed to largely eject its type system at the earliest opportunity that doesn't negatively impact the compiler. This will naturally constrain the capabilities of the generic programming constructs, much as it did in Josh Bloch's Java generics implementation. Go is going to be torn between the minimalism of its creators, and the desires for modern capabilities expressed by its vocal user base. If this were a movie, it would be "A Beautiful Mind".
- skybrian 4y agoThere are vocal critics and sometimes they’re not wrong about some of the flaws, though often they make too big a deal of them. But I think there are also people who appreciate stability? It’s a language people complain about in part because it’s popular. In this case, Tim Bray tripped over “Go doesn’t have inheritance, but it does have embedding” and ran into a couple limitations in the first version of generics, but he’s not making any wider claims.
- xyzzy_plugh 4y agoLuckily for us the Go core maintainers do not capitulate easily to the complaints from the vocal minority.
- cy_hauser 4y agoI thought the genericists were the majority? It was the top most requested feature in the Go surveys for years.
- xyzzy_plugh 4y agoJust because it's the most requested feature doesn't mean it's a majority, it's not even close: > Among the 26% of respondents who said Go lacks language features they need, 88% selected generics as a critical missing feature. From https://go.dev/blog/survey2020-results https://go.dev/blog/survey2020-results Most folks I work with are among the 74% who are content with the language and are mostly cautious of the complexity associated with generics.
- cube2222 4y agoFrankly, having used Go for years, I started using generics right after they were released and I'm very happy with them so far. Sure, the performance characteristics could be improved, but other than that they solve all my main pain points I wanted them to solve while being constrained enough to not result in ivory towers on every corner. My main pain points having been duplicated functions for each type as well as data structures.
- bsudnshdbd 4y ago
- haolez 4y agoWhere you see "modern", I see complicated type systems that solve the least interesting pains when programming. What they do provide is a feeling of "solving a puzzle" when all your type signatures are matching your usage patterns, but then again it's easy to mistake the puzzle solving with actual productivity. Rich Hickey's talks can be very insightful in this regard. He surely convinced me :)
- throwaway894345 4y agoI think type systems have diminishing returns. I’ve used Python professionally for 15 years and a whole lot of time is wasted trying to figure out exactly what properties a given parameter must have. In the best case, you have stuff like “file like object” which doesn’t tell you if it just needs a “read()” method or also write, seek, close, etc. In the common cases you have annotations that are incorrect or outdated. On the other extreme, you have Rust and Haskell type systems where you spend gratuitous time on pacifying the type checker for negligible quality gains (arguably negative gains). I always felt Go hit the sweet spot.
- klabb3 4y agoViolently agreeing with this. Both extremes hinder productivity, just at different times during the development cycles. It's easy to confuse type systems with correctness or clarity of thought. I've fallen victim to this myself – "oh this didn't map well to Rust's type system, I must be thinking wrong", kinda. It tickles this ocd nerve and I lose focus on the problem I'm trying to solve, often without realizing that I'm just wasting time. I also like Go for these reasons. It's dumb and predictable, which let's me focus on the problem I'm solving. My only annoyance is really verbosity and small amounts of boilerplate.
- swid 4y agoHere's the way you can solve the first problem with generics in go, almost exactly as he described: https://go.dev/play/p/GrLYBj3N0jr https://go.dev/play/p/GrLYBj3N0jr The trick is to define the fish tank like so: type fishTank struct { container[fish] }
- timbray 4y agoThanks, yep, that works. Updated the blog piece and will improve the Quamina code a bit too.
- masklinn 4y agoFWIW the original works as well, but because you're using a typedef (not an alias) you have to convert from your own type to the underlying: func (t *fishTank) fishCount() string { return fmt.Sprintf("How many fishies? %d!", (*container[fish])(t).size()) } that's because type fishTank container[fish] creates a completely new and independent type with container[fish] as the underlying implementation. An alternative is to just alias: type fishTank = container[fish] however in that case you can't define methods on fishTank, because it's literally just a shorthand for container[fish].
- timbray 4y agoOK, that `(*container[fish])(t).size())` idiom is um non-obvious. Somebody needs to write a nice simple bloggy step-by-step walk through all these gyrations.
- masklinn 4y agoIt's not really an idiom. The normal conversion expression is T(v) but that doesn't work if T is a pointer type, because it's parsed as *(T(v)) so the types don't match. So you need (*T)(v) to ensure the type part includes the pointer specifier.
- CraigJPerry 4y agoNot a Go developer but i've been playing with Go for a toy project, really just to learn it. I had a tiny play with generics in Go when i needed a "min(int, ...int) int" func // --- There's no int min() in stdlib ----------------------------------------- // Option 1. use math.Min() which is defined for float64, benchmarks comparing this with Option 2 & 3: // BenchmarkMinImplementations/math.Min-8 261596238 4.282 ns/op // BenchmarkMinImplementations/minGeneric-8 588252955 2.037 ns/op // BenchmarkMinImplementations/minVariadic-8 413756245 2.827 ns/op // Option 2. Generics in Go 1.18, this is generic over int and float but can't support a variadic "b" func minGeneric[T constraints.Ordered](a, b T) T { if a < b { return a } return b } // Option 3. I was aiming for a lisp-style (min 1 2.3 -4) and this is variadic but it can't also be generic in 1.18 func minIntVariadic(a int, bs ...int) int { for _, b := range bs { if b < a { a = b } } return a } Coming from Java there's less to get your head around, e.g. super vs. extends isn't a thing here but otherwise similar.
- Groxx 4y agoYou can do variadic args, you just have to explicitly label the type in both arguments for some reason, like you did for the non-generic version: https://go.dev/play/p/L6psz_WdETM https://go.dev/play/p/L6psz_WdETM (I'm aware it's not actually using the varargs reasonably, typing code on a phone is a pain)
- CraigJPerry 4y agoHa! This is awesome, thank you
- gambler 4y agoWhen I started using Go generics, they turned out to be completely useless for the use case I wanted to solve the most: functions to use in Go templates. Something is basic as adding two numbers is not solved by the core library and becomes a challenge with about a dozen numeric types. You can use a type switch and cast interface{}/any to float64 (which is a hack, but one that works in practice). What you can't do is use generics, because they require instantiation at compile time, which cannot happen in templates, because those are dynamic. Extremely disappointing. Another disappointing thing is the fact that Go type inference is not good enough to have syntactically compact lambda functions. So now, even though it's possible to write code that does filter-map-reduce kind of stuff, it's verbose, slow to write and hard to read.
- yazaddaruvala 4y agoHey Tim, a long time ago I came to your office in Vancouver to talk about Ruler. Quamina seems very similar. Are you learning Go with a familiar project? or is there another motivation for Quamina?
- timbray 4y agoSee the piece about Quamina on my blog, it's the one before this one.