22 ms·
Trying Out Generics in Go
- bullcitydev 5y agoAuthor here. I just removed the previous section I had around using build tags because I realized I was using `// +build 1.18` instead of the correct `// +build go1.18`. Oops.
- travisd 5y agoFYI: Looks like code blocks are unreadable on mobile (probably a overflow: hidden CSS rule somewhere that truncates lines and doesn’t let you scroll).
- bullcitydev 5y agothanks! I'll check it out!
- bullcitydev 5y agoJust fixed it. Thanks again!
- grey-area 5y agoShame build tags aren't part of the language with a proper syntax check instead of magic comments.
- giovannibajo1 5y agoCan you show me an example of a language that does syntax checking of build-system related directives at compile time?
- grey-area 5y agoC++ #pragma, #include? Golang import statements? I see why they did this for the Go 1 guarantee, but would prefer if they used a keyword and had a defined restricted syntax for it. There are a growing number of these comments and they’re poorly documented and spread around different tooling. It’s kind of a hack. https://blog.jbowen.dev/2019/09/the-magic-of-go-comments/ https://blog.jbowen.dev/2019/09/the-magic-of-go-comments/
- kevin_thibedeau 5y agoNim. C++ attributes (coming soon to C).
- Groxx 5y agoZig. Rust. The others in this thread. There are quite a few. Go chose possibly the weakest-safety option of them all.
- dsnr 5y agoThat’s a welcome feature even though I don’t like the syntax. But Go could become the perfect language if they just fixed error handling and a couple small annoying quirks.
- fourseventy 5y agoI like the go error handling
- dewey 5y agoI've never seen a person who writes Go for more than a few weeks complain about the error handling. I certainly don't mind it myself. Is it really a problem people have or just somewhat of a meme at this point?
- ghayes 5y agoIt might also be self-selection that people that truly dislike the error handling simply avoid golang. I’d really be interested to see how well go generics handle the Result type.
- jatone 5y agomeme. its like the least annoying thing I deal with on a daily bases when programming. oh no... I have to handle an error....
- stouset 5y agoHi, I'm one of these people. I used Go for about six months and eventually abandoned it to pursue Rust, a decision I've been extremely satisfied with. The longer I used Go, the more I grew to hate it and error handling was one component of that. Well over half of Go source code in practice is dealing with errors, and somehow the Go ecosystem has convinced themselves that "verbose" is the same as "explicit" when it doesn't need to be. The worst problem isn't that it's just a lot of excess code, it's that it makes all sorts of very simple and common programming tasks ridiculously unwieldy. The most obvious example is calling a fallible method, doing something to the result, and returning it (or the error). This is one single character in Rust but a minimum of four lines—with branching—of copy-pasted boilerplate in Go. Which isn't a lot in the abstract, but then you multiply that by hundreds of times and now I have read, lex, parse, and mentally discard the majority of pages of source code that's doing something that could be done in ten lines with a massive incerase of clarity in a more reasonable language. You've probably "never seen" us because we felt very let down by the overpromise and underdelivery of go and we left.
- Smaug123 5y agoI think you're making it easy on yourself by choosing "a library whose sole purpose is to stamp out the boilerplate needed to work around Go's lack of generic algebraic data types" to demonstrate how good generics are :P
- bullcitydev 5y agohaha for sure. but still better than code generation IMO ;)
- hmmdar 5y agoAnother way to do `Option` without pointers could be similar to the following with a struct with two members. type Option[T any] struct { v T isSet bool } func NewOption[T any](v T) Option[T] { return Option[T]{ v: v, isSet: true, } } func (o Option[T]) Get() (v T) { if !o.isSet { return v } return o.v } func (o Option[T]) IsSet() bool { return o.isSet } With this pattern you're able to use `Option` as a value without pointers. var o Option[int32] o = NewOption(int32(1)) fmt.Println("value:", o.Get()) fmt.Println("is set:", o.IsSet()) Alternative separate `Get` and `IsSet` methods, is to combine them into one, similar to map look up pattern. func (o Option[T]) Get() (v T, isSet bool) { if !o.isSet { return v, false } return o.v, true } var o Options[int32] v, ok := o.Get() // zero, false o = NewOption(int32(1)) v, ok = o.Get() // 1, true
- 1_player 5y agoI don't understand your example, `isSet` is always true and can never be false. Missed something?
- hmmdar 5y agoThe only time `IsSet` would be false is when `NewOption` was not used to initialize the value. e.g. var o Option[int32] or could have `None` helper func None[T any]() Option[T] { return Option[T]{} } o := None[int32]()
- gnulinux 5y agoIt works because it's false when uninitialized (as default value). So if not initialized it represents Nothing value. When it is initialized it's Just T.
- iambvk 5y agoI agree with `Get` returning `(T, bool)` I don't see why one would want to return an `error`.
- jamespwilliams 5y agoOne annoying bit about Go's generics is that you can use type parameters in functions, but not in methods. So for example, maybe you'd want to write a Map function for the Optional type in this article, which returns None if the option is None, or calls a given function with the value of the Optional otherwise. You'd probably write it like this: func (o Option[T]) Map[U any](f func(a T) U) Option[U] { ... } But that doesn't work: "option/option.go:73:25: methods cannot have type parameters" The type inference is also a bit limited, e.g: let's say you have a None method: func None[T any]() Option[T] { ... } And you call it somewhere like: func someFunction() option.Option[int] { if (!xyz) { return option.None() } // ... } it isn't able to infer the type, so you have to instead (in this case) write option.None[int](). Generics is a super cool addition anyway though. Edit: I just found https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#No-parameterized-methods https://go.googlesource.com/proposal/+/refs/heads/master/des... which has some details on why method type parameters aren't possible.
- tubby12345 5y ago>One annoying bit about Go's generics is that you can't use type parameters in methods. i haven't written go in a long time (generics would/could get me to go back to it) but are you saying that functions can't be generic? or is members here vernacular for class (struct?) associated functions? i thought those were called "receivers", which you mention further down. so it looks to me like you're saying that functions can't be generic. to which i ask: wtf is the point of generics when functions can't be generic...?
- jamespwilliams 5y agoYou can use type parameters in functions, but not methods. Methods are functions which have a receiver, so: // This is a function, you can use type parameters here: func Foo[T any](g T) { ... } type bar struct {} // This is a method, you can't use type parameters here: func (b bar) Foo[T any](g T) { ... } In the second case, "Foo" is a method which has a "bar" instance as a receiver. I've edited my original post to make it a bit clearer.
- jaytaylor 5y agoHonest question: Do you think Generics will be an overall win for the Go language, or will they be overused / end up making code harder to read / harder reason about? I kind of hate looking at Go code containing generics. What previously was elegant and easy on the eyes is now annoying and muddled by the additional layer of abstraction. I'm saying this with sadness, as someone who fell in love with Go back in 2012 and still writes it at least weekly. Is it a better bet to move on and go full Rust, rather than bother with wherever the goggle golang train is headed? p.s. Even though code generation is [also] annoying and perhaps not ideal, I've rarely needed to use it and kind of liked that it was inconvenient - forcing me to think about problems differently. Certainly some problems will benefit from the addition of generics, but is it really enough to justify the added complexity? I wonder if this is a case of tragedy due to vocal minority. p.p.s. Generics in other languages like Java or Scala seem fine, as they are "kitchen sink"-style "all things to all people" languages. Such behemoths are nearly always clunkier and less easy to read than pre-generics Golang.
- marcosdumay 5y agoHum, I've never seen generics making code harder to reason about in any language, except, of course for C++, where they are hacked over text. If they will make code harder to read, that's up to syntax. I don't know how all that will look up on the end, but it should be reasonably easy to just write an example.
- spinny 5y ago> Hum, I've never seen generics making code harder to reason about in any language, except, of course for C++, where they are hacked over text. do you mean templates? i was under the impression that c++ generics == templates, but after a google search found out that c++ has both (at least according to microsoft), not surprised https://docs.microsoft.com/en-us/cpp/extensions/generics-and-templates-visual-cpp?view=msvc-170 https://docs.microsoft.com/en-us/cpp/extensions/generics-and...
- tsimionescu 5y ago
- bruce343434 5y agoHow does Go handle the ambiguity between [] meaning generics and [] also meaning array?
- pphysch 5y agoYou can use optional(?) parenthesis to make it extra clear. []MyContainer[T] // slice of generic struct or interface can/must be written as [](MyContainer[T]) but ([]MyContainer)[T] isn't a valid use of generics anyways.
- bruce343434 5y agoWhat about accesses? Seems like this would require at least some context in the parser. And what about the human parser? Do you get confused between arrays, array accesses, templates? Or do you get used to it? T int = 5 myarray[T]
- pphysch 5y ago'T' is a completely normal identifier, it is merely the conventional one used as a type parameter. But you can also use Type, MyType, etc instead of T as that parameter identifier. The compiler can easily detect if the thing in the [] is a int or type.
- uluyol 5y agoIn most cases there is no parsing ambiguity. In the cases where there are, you need to use parenthesis to clarify.
- unix1 5y agoI too was playing around with Go generics. I wrote some naive concurrent filter and fold (reduce) functions for slices and maps here https://github.com/unix1/gostdx https://github.com/unix1/gostdx if anyone is curious how those would feel.
- amelius 5y agoSide question: Are there any languages that transpile to Go?
- iamgopal 5y agoThere is python to go transpiler from google.
- Laremere 5y agoMy opinion with 9+ years since first learning Go, multiple of those using it for a full time job: Putting the end first, my rule of thumb for using generics in Go is: Don't go down the OOP road of over planning and programming with fancy type work. 99% of the time, the common Go programmer won't need to write any generics. Instead, just focus on actually solving the problem and manipulating the data like you would normally. If you encounter a place where code is repeated and complicated enough to be worth a new function, move it to one. If you find yourself repeating multiple functions but with different data types, turn that into one generic function. Generics are an incredibly useful addition to the language that I'll almost never use. Really to be more precise, Go has had some generics this whole time: Maps, slices, arrays, and channels all have type parameters, and have covered the vast majority of my needs. There are a few times where I've wanted more general generics, though: - The sort and heap packages are rough to use. You need to specify a bunch of nearly identical functions just to get them to work on any custom type. The generic versions (not coming in 1.8's standard library, iirc) will be much easier to use. - Was writing an Entity-Component-System game for fun, and needed a weird custom container. Turned to code generation, and really that turned out to be necessary anyways because it did more than any (non-metaprogramming) generics could do. - We had one very complicated multiple Go routine concurrent data structure that needed to be used for exactly 2 different types. Others were writing the code, and very afraid of using interface{}. This is despite there only being a handful of casts. In reality if they caused a bug, it would be found immediately. There's a strong hesitation around type safety dogma that isn't risky in practice. Still, generics would've been the preference here. - I was parsing WASM files, and there's a common pattern for arrays where it encodes the length of the array, then that many objects in a row. It led to a lot of minor code repetition. Replacing that with a generic function that took a function to parse a single object, and returned the array of those objects was a nice, but relatively minor win. On the other hand: I've never really been bothered by having to do sets like map[int]struct{}. There was one case where I saw someone put set operations out into a different library. I eventually found to my dismay that the combination of how the set library was used, and how it was implemented caused a performance critical part of the code to be several orders of magnitude slower than it needed to be. Had this code been more idiomatically inlined, this flaw would have been more immediately obvious. I really don't like seeing map/reduce/filter type functional programming coming into Go usage. This type of code tends to need more dramatic changes due to minor conceptual changes, more than direct procedural code does. Also like the set example, how you iterate and manipulate objects can have large performance implications that using such functions hides away.
- geoka9 5y agoIt could be my personal negative experience with maintaining code that overuses generics in other languages, but I have reservations about this feature. I almost never need them, but on the other hand I don't feel too good about having to repeat myself when writing library packages. I almost feel I would be happy with generics in Go if Go made them illegal in anything but libraries (not allowed in package main, maybe? Or not allowed in a package unless it gets imported by another package?).
- FpUser 5y agoYes generics / templates are mostly useful for general libraries. But if you afraid of programmers doing stupid things just for the fuck of it it is better not to hire such programmers, warn them if they're juniors or just hit them with the bat on code review.
- FpUser 5y ago>"In case you’ve been living under a rock ..." Not really but getting to know Go is not on the list of my priorities. Looked at examples. Many languages use angle brackets for generics and templates but in case of Go they had to do it their own way and use square brackets that most programmers would perceive as an array. Funny.
- eyelidlessness 5y agoWhich isn't unprecedented, and lots of very widely used languages use other brackets for array literals or other array literal syntax. Even in languages which use brackets that way, they're often overloaded (see both TypeScript and JavaScript, but also JSDoc type annotations).
- nsonha 5y agoscala uses square braces. I don't even use scala and I like it, not having to hold shift is a win and language designers should think about how a programming language is typed because it's literally 50% of the reasons why a syntax is the way it is.
- random314 5y agoThis is a phenomenal achievement, that I didn't expect to see in my lifetime! Gives me hope that P vs NP will be resolved in my lifetime too!!
- nahconan 5y agothis is why hapas are superior to wh*tes
- xyst 5y agoperson deleted 95% of code, but what about performance? is it more or less the same as the non-generic implementation?
- marksomnian 5y agoIn theory yes, because generic types are monomorphised, so I would expect performance to be no less bad than implementing each individually.
- fnord77 5y agoI see they went with []. Scala does this and it annoys me because [] also denotes arrays. Unlike <> which doesn't have any overloaded meaning.
- Jtsummers 5y agoThey wrote about the syntax a while back, one reason they avoided <> is that they lack parse-time type information to properly differentiate between certain cases like: a,b := w < x, y > (z) There are two valid parsings of that if generics use <> and at parse time it isn't known which one to use. Either: a, b := [boolean expression w < x], [boolean expression y > (z)] or a, b := [generic function w] [with parameters <x,y>] [applied to (z)] https://groups.google.com/g/golang-nuts/c/7t-Q2vt60J8 https://groups.google.com/g/golang-nuts/c/7t-Q2vt60J8
- nahconan 5y agoFinally, I don't have to hear "lol no generics" any more.
- deleted 5y ago[deleted]
- 3np 5y agowrong thread
- dmnd 5y agoAmusing that this post goes from > My first response when the plan to add generics was announced was “meh”. In my 5+ years working in Go, I can probably count on one hand the number of times that I felt like I really needed generics. Most of the code I write in my day job is very specific to the domain and doesn’t fit the use case that generics aim to fill. to > I love that I was able to delete 95% of my code because of generics.
- 5e92cb50239222b 5y agoThat's why you need some breadth of experience with many different languages, folks. That's exactly why you don't limit yourself to a single (extremely simplified to the point of stupidity) language.
- skinkestek 5y agoThat's also why one should take a look at back and think if it is smart to uncritically said generics were just dumb. Same with people who unconditionally recommended WhatsApp not that long ago. Or people like me who told everyone Google was still nice and a driving force for good until a few years ago ( yes, I still have some hope that they will change their ways and don't think others are much better but I am somewhat bitter and I don't give them the benefit of doubt anymore :-| )
- dm319 5y agoIt's interesting because I remember all the early discussions against generics in Go centred around "what Real World scenario do you need it for?" An argument against generics was that people found it hard to find examples that were 'real' where generics would be beneficial, and so because it was rarely needed the question of whether the language should be drastically bodged/ruined/adjusted for this feature was called into question. In retrospect you had a self-selecting population of people who loved Go and presumably didn't have much use for generics, whereas people who did presumably used something else. I guess all we can learn from this is that human imagination is poor, and many of us need the thing in our hand to work out what we can do with it.
- coldtea 5y ago>My first response when the plan to add generics was announced was “meh”. In my 5+ years working in Go, I can probably count on one hand the number of times that I felt like I really needed generics. And then the author goes to admit that they had written a whole library with the kludge that is textual code generation "to support both primitive and custom types".
- dgellow 5y agoSomething I don’t often see mentioned in these discussions about generics: generics as a feature is massively important for library authors, not so much for library users. So of course if you’re mostly spending your time writing business logic and web APIs you don’t encounter the need for generics that often. But when you try to write for example a library for a data structure while keeping some type safety (so not relying on interface{}), you absolutely need generics.
- deleted 5y ago[deleted]