13 ms·
We have a very large Go codebase here at Stream and not having generics is just not really as big of an issue as you think it is. There are plenty of work aroun
by tschellenbach 6y ago
We have a very large Go codebase here at Stream and not having generics is just not really as big of an issue as you think it is. There are plenty of work arounds if you get used to not having generics in the language. The fast compile times of Go are amazing. I was doing some Kotlin a few weeks ago and the difference is crazy. Go: Install deps, compile everything done in 5s. Doing the same in Kotlin, laptop freezes, android studio freeze, time to get a coffee :)
That being said it would be really nice to have some reusable map type structures that handle GC better than the default maps. Fingers crossed.
- egeozcan 6y agoI actually just need generic Sets. Generic map/reduce on slices wouldn't hurt too. OTOH, it's 2021 and look at what we are wishing. My love/hate relationship with golang is like the one I have with Apple.
- jjtheblunt 6y agoisn't generic Sets easily implemented with map being already generic?
- atombender 6y agoOne challenge there is that identity is only supported for some built-in types — only primitives, and structs of primitives; no pointers, slices, maps. If you want a set of some complex kind of value that contains non-map-indexable types like slices and pointers, then you have build an indirection around it. A good set implementation needs to support a comparison operation. I really wish this existed for Go maps, too.
- fileeditview 6y agoYes and that's probably why there is no set in the go std lib. You just can use struct{}{} as (empty) value in a map.
- joshlemer 6y agoI thought the idomatic approach was to represent sets with map[key]bool
- philosopher1234 6y agoThe bool is meaningless, so empty struct is more clear as it contains no state.
- RHSeeger 6y agoAs someone who finds "indicating intent in the code" an important thing, I must admit I find this concert slightly horrifying. A Map and a Set are two different things and which one you use conveys some intent as to what you mean by your code. I get that it works, but it would still make me unhappy to do.
- Jtsummers 6y agoIdeally, if the language/standard library provides maps but not sets, and you wanted to use the idiomatic set = map of type -> bool approach, you'd create a wrapper so that intent is preserved but users don't have to know about the backing mechanism. Of course, it's obnoxious if everyone has to do this themselves and the language lacks generics so you have to write this once for each potential type.
- philosopher1234 6y agoWrappers that don't wrap very much aren't worthwhile IMO. Its like getting an amazon box with a fedex box inside. Just give me the package itself.
- Jtsummers 6y agoShallow wrappers that don't wrap much (now) but convey intent better are valuable if you buy into the idea of modularity and encapsulation in general. Some reasons (probably more out there): 1. The now-provided interface can more clearly express what the code is intending to do (better names for the operations you're providing than the underlying system has, remove_from_end to pop or dequeue) 2. Hide methods of interacting with the underlying data structures that you don't want people to use (use a C++ vector as a stack, but don't want random access) 3. You can replace the underlying mechanisms at will without impacting the users If you just wrap a vector in your own vector class and otherwise provide the same operations (or a limited set of operations but for no good reason to restrict usage), sure, that's moronic. But if you wrap a vector class in a "BigNumber" class and provide operations like add, subtract, mod, etc. then value has been added. Same thing with the idea of wrapping a map in a set interface.
- coldtea 6y agoNot if you need to implement set operations (union, intersect, etc.) on your own each time. Generic maps as sets are only ok for membership checks.
- masklinn 6y ago> isn't generic Sets easily implemented with map being already generic? Since Go has neither generic functions nor generic typedefs you can't implement a Set with a generic key type on top of map, you have to reimplement all the set operations for each key type you use.
- jjtheblunt 6y agoI see what you mean there.
- coddle-hark 6y agoI think map[T]bool is already a pretty good set; the only things you can do with sets are insertion, deletion, iteration and checking for existence and they're all well-supported. Of course, if you need a concurrent set you're right back in type system hell.
- erik_seaberg 6y agoYou can’t write intersection, union, difference, subset (contains all), or powerset as reusable functions for any element type. The idiomatic thing for now is to rewrite them as loops over and over, but that’s error prone, hard to read, and not a good use of time.
- EdwardDiego 6y agoThis was the exact issue I hit. There are set libraries for Go, but they only handle standard types. The for loops got old quick.
- simiones 6y agoIn addition to that, map[T]bool only works if T is one of the few types that Go can check for equality automatically. You can't define a custom equality (+hash) function for your type and use it with the built-in map.
- EdwardDiego 6y agoFor some uses of a set, yeah. For intersections / unions etc. nope. Then it's back to for loops, so many for loops in Go.
- glouwbug 6y agoEh, even C supports clean templated type safe containers: https://www.github.com/glouw/ctl https://www.github.com/glouw/ctl Why Go is so far behind is behind me
- bierjunge 6y agoThere is a big difference here. The draft is about adding generics to the Go stdlib, not about if it's possible. Is it even possible? Yeah, there are some implementation (e.g. https://github.com/cheekybits/genny https://github.com/cheekybits/genny). So Go is not "far behind" C, which also does not have generics (or did I miss something in C11/C17?).
- jeremyjh 6y agoWhat is the workaround for having a nice ORM library?
- konart 6y agoNot using an ORM.
- tschellenbach 6y agoThis one is mostly excellent: https://github.com/go-pg/pg https://github.com/go-pg/pg
- jacques_chester 6y agoIf we're dealing in anecdata, mine is that "Go compiles fast!" is true right up until something in your dependency tree hauls in kubernetes repos, perhaps multiple times. Thanks the "first principles" design of go mod, that's becoming increasingly unavoidable. Kotlin does compile much slower than I would like, but at least I only haul in one version of libraries and 0% of it is generated code. Java is basically instant for me on the same codebases.
- dhagz 6y agoI'm currently working on a project that depends on the k8s APIs and I haven't noticed ballooning compile times. I'm only pulling in k8s.io/api, k8s.io/apimachinery, and k8s.io/go-client though.
- jrockway 6y agoI have the same experience. I maintain lots of software that directly depends on k8s.io/go-client and it feels the same as any other Go project.
- jacques_chester 6y agoMy code before a transitive dependency pulled in the k/k universe took milliseconds to compile. Afterwards it took about 10 seconds to compile. Laboriously compiling thousands and thousands and thousands of lines of nearly-identical code turns out to be much slower. There are no clever shortcuts for a compiler that cannot deduce a higher intent.
- dilap 6y ago> Thanks the "first principles" design of go mod, that's becoming increasingly unavoidable. That's interesting, could you explain this more? Context: I used to use Go a lot, but mostly haven't since Go modules, and I'm curious to know the details of the problem and why it happens.
- jacques_chester 6y agoIt doubles down on Go's assumption that git repository === a proper package/module system. It mixes up URLs and URNs. If your git repositories aren't tagged just so, then go mod throws its hands up and simply invents a whacky snapshot version. Because it can't itself properly determine "earlier version" from "later version" on that snapshot, you often wind up with multiple snapshots from the same repo, not infrequently transmitted through other dependencies. This is just jolly good fun when it turns out that your dependencies are pulling in incompatible versions of things. Since the official Kubernetes policy for downstream consumers is "we don't care about downstream consumers", it happens more quickly than one would expect. As much as I have hated playing whack-a-mole with Maven or Bundler, I hate even more playing whack-an-adamantium-and-invisible-with-xray-eyes-mole against go mod.
- lkramer 6y agoThis is not the first time I argue this case, but I would go a step further and say that not having generics is feature of Go. There are plenty of languages out there with Generics. I use several of them. I use Go when that suits me, and it's typically for cases where high readability trumps doing a lot of magic with generics. I think only once or twice have I thought to myself, "this would have been better with Generics".
- deskamess 6y agoThe introduction of generics would not change your workflow though. You could still happily "not use it" and keep matters readable. Others who wanted it, would use it.
- lkramer 6y agoMostly concerned about having to read other people's code that uses it.
- coldtea 6y agoIf others people code uses it, then those other people deemed it useful. So the argument for not having them now becames either: (a) they rather not have it available, because you personally don't find it useful (b) those using generic don't know what they're doing, and only people not using generic are smart, so it's better to not have them to prevent the clueless from being able to use them
- lkramer 6y agoLike I said, I use (and like) several languages that have Generics, and when I need to do something where it makes sense, I can reach for those. For me there was an advantage in having a language where it wasn't an option. Your (b) scenario is quite a strawman. I have seen plenty of good code using Generics, but sure, there is subset of code written using Generics that is not good, and I think it becomes easier to obfuscate code and make it hard to read if you have Generics, that might just by my bias, and I think I'm tainted from C++ and hopefully it will never become as bad as what you can encounter there. I'm not trying to make out that Generics have no place in Computer Science. I was trying to make the case for it being nice that there was a language that didn't have it, and was building on the grandparent saying that he didn't miss it that often, which mirrors my experience with Go.
- johnnycerberus 6y agoMeasuring against Kotlin or other languages in this category like Scala is not the right thing to do. Also Kotlin is known for its slow compiler though JetBrains promised that the situation will improve. I would use Scala or Kotlin for everything data but when it comes to low-level networking/infrastructure I wouldn't even touch anything else but Go. Go is in the perfect sweet spot for this task. The Goldilocks Zone of network programming.
- BoorishBears 6y agoIt's such a truly terrible comparison because they didn't compare Kotlin, they compared Android! The insane amount of time of overhead an Android project has over a "plain Kotlin" from resource packing to dex stuff to desugaring to ProGuard, it made me question if they're speaking in good faith for the rest of the comment... My ktor projects on the other hand compile incredibly quickly, and with hot reloading it's even more seamless to iterate
- random5634 6y agoI'm pretty convinced they could have added some additional structure libraries to the standard library and called it good. Some map stuff / set stuff etc. For many its really not a big deal not having generics, and makes code SO much easier to follow (and compile / debug etc).
- shadowgovt 6y ago> We have a very large Go codebase here at Stream That's exactly what generics are supposed to solve! ;) No, but seriously, I'll be interested to see if they can pull off maintaining the compiler performance while adding support for this new feature. I've had to hand-write a lot of code that I'm excited about a generics solution automating, but automation can have a price, you're exactly right. I've worked on a C++ codebase before that couldn't physically compile on my machine because it blew stack on template instantiation recursions (issue never noticed because the original developer had a better machine).
- ramoz 6y agoIn our large production project we have a very simple Go arch that enables 15+ microservices. Im not sure I see the value of generics either other than complicating a rather complicated distributed architecture with abstracted implementation. It's beautiful that we still import 6yo packages that are clear, concise, and work as needed. The package landscape with generics doesnt seem great.
- saturn_vk 6y agoI don't think generics will suddenly make your, or anyone else's codebases more complicated. It might simplify code that's currently doing type assertions
- stickfigure 6y agoI remember thinking something similar about my Java 1.4 codebase back in the early 2000s. It's fine, right? What could I be missing? The answer is: A lot.
- sam_lowry_ 6y agoI miss Java 1.4. It was small and concise. Java 5 added so much that none knows all of it. Just look at the length of Java Generics FAQ. It's hilarious.
- skinkestek 6y agoI've - read that from start to end exactly zero times, - picked up generics in a day or two, - struggled with advanced types twice, half a day each time - struggled with type erasure twice, also about half a day each time. Meanwhile generics often saves me a number of minutes pr hour and makes everything cleaner and easier to read. I used Java before generics but once it arrived I never looked back and neither did anyone i know.
- karmakaze 6y agoJava before 5 wasn't a language, it was a library and number of jvm implementations. It wasn't until Java 5 that there was a memory model spec that defined how stuff was supposed to work.
- jacques_chester 6y agoPeople from the post-1.4 era of Java aren't aware how huge code generation was before generics showed up. And then all that momentum, all the tools, the books, the conference presentations, the code ... all of it. Pop! It died. Because code generation sucks. It is the worst solution to any problem solvable by a type system.
- BoorishBears 6y agoMy favorite example of generics wonkiness was when I needed a channel to wrap an untyped channel to avoid "infecting" every call site for a utility function with untyped pointers. I thought it was madness, but bringing it up to a very large Golang group and get "nope channels are cheap! That's fine! There's repetition but it's easy to follow" I've said before, my personal take is use Go, get a feel for the Go mentality, then take it with you to another language. Go is just too stuck between low level and high level for me personally. I'd rather go under with Rust or over with Kotlin or C#
- randomdata 6y agoThe biggest challenge is avoiding the tricks from languages that allow you to use the language to paper over questionable design choices. Go requires that you get the design right up front and provides few escape hatches to save you if you mess up. Which is a good thing, but makes the language (not just the syntax) difficult to learn compared to others.
- TheDong 6y agoCan you give examples of specific language features of go that prevent you from making questionable design choices, that require you to "get the design right up front"? When I hear that, the first things that come to mind are things like haskell's IO monad, which forces you to model IO better than go or most other languages, or haskell's other state monads which similarly force you to model state more explicitly. I think of rust's lifetime and ownership system, which forces you to correctly model the ownership of types and prevents quite a few bad design patterns (which I see constantly in go btw; the number of times I've seen races due to multiple goroutines writing to a struct is large, the number of times I've seen incorrect synchronization that rust would have prevented is large). I can't think of anything in go that pushes you towards designing your code well in go, especially when compared to languages with more complete type-systems.
- randomdata 6y ago> Can you give examples of specific language features of go that prevent you from making questionable design choices, that require you to "get the design right up front"? No, obviously. I said that Go doesn't give you an escape hatch if you screw up. It does nothing to protect you from screwing up. I specifically said that the challenge was in learning how to not screw up as the language doesn't help you deal with or avoid design mistakes.
- TheDong 6y agoAaah, I read "Go requires that you get the design right up front" totally wrong, as in "go prevents you from getting the design wrong", not "go lets you get the design wrong, and then doesn't help you". I still don't get your contrast to other languages though. Are there specific language features other languages have that let you paper over crappy designs?
- kitd 6y agoGenerics make working with reactive APIs much easier, which I think fits with a large chunk of Go's target audience (b/e networked services) quite closely. Writing APIs to deal with futures, etc, is much easier when you can chain parameterised functions together.
- brundolf 6y agoIt seems to me like generics are extremely important for "library code", and not super important for "application code" (and in fact they can sometimes create more confusion than they're worth in the latter context). Go also seems like a language that thrives in smaller-scale, application-focused contexts (microservices being the obvious example). So in this light, and with the basic generic data structures supplied by the standard library, it seems to make sense for "user-level" Go code to generally be better-off without generics Of course the line between "library" and "application" code isn't well-defined (especially if you consider libraries outside of the standard one), which is probably where most of the pain-points are coming in
- enneff 6y agoI generally agree but I note that any substantial project becomes largely composed of “library code” itself.
- brundolf 6y agoRight, and that's where the question gets muddy Though again, Go as a whole seems ill-suited for scaling to larger projects because of lots of other limitations on its type system, reliance on conventions, implicit-defaults, etc. Which makes it well-suited to (and often used for) things like microservices, where each actual codebase is smallish. Codebases like these will tend towards having less "library-like" code anyway, which means they don't need generics as badly. There's synergy here in the language design. So I guess what I'm saying is: leaving out generics seems like the more "Go-like" direction, will dovetail better with its overall philosophy, etc, and isn't without advantages. But it would also mean kneecapping the language when it comes to certain use-cases that it's never going to be great for anyway. It's the classic "opinionated" vs "everything for everybody" dichotomy
- philosopher1234 6y agoWhy do you say Go has trouble scaling to large code bases? Is that something you'd expect, or something borne out by the evidence? And if so, what is the evidence?
- strken 6y agoWhen I worked at $big_company, we used a lot of code generation and reflection to work around the lack of Go generics in things like API interfaces to other services and test mocks. This was far from ideal, because it significantly increased compile times, and some things stopped being typesafe or had unfortunate type-related bugs.
- wtetzner 6y agoOCaml has generics (and global inference!) and compiles very fast.
- Tainnor 6y ago> Doing the same in Kotlin, laptop freezes, android studio freeze, time to get a coffee I work in Kotlin every day and my laptop never freezes. I'm sure compile times are shorter in Go (after all, that's one of Go's main selling points). But compiling Kotlin code doesn't seem to be a huge bottleneck in my experience (and I have worked in some other languages that had truly atrocious compile times cough Swift cough). In fact, it usually takes longer for Spring to boot than for the Kotlin code to compile.