Golang Generics
I quit Go over the lack of generics once. I start with this to show that I'm not someone stuck in my ways, I was won over. And I fought tooth and nail. I learned Go for the first time in 2012. After a few weeks of playing around with the language, I decided that it was terribly designed, overly opinionated, too cumbersome and the lack of generics showed just how immature it was. And I quit. For about a year.
But I kept coming back to the few things I liked. I liked the simplicity. I liked the compile speed. I liked the run speed (I was coming mostly from Python and PHP). And I really liked the concurrency. So after a year, I decided I'd come back, and I'd just hack my way around the shortcomings of the language.
The first two things I did when I came back were figure out the reflection library and figured out how to abuse "unsafe" to get around most of the language constraints (for instance, you can gain access to and modify unexported fields with unsafe). Thus armed, I was prepared to do battle with the compiler.
Then something funny started to happen. The things I hated started to make sense. The compiler would warn me about an unused variable, so I'd go to do what I always did (some sort of no op to trick it) only to find what would have been a subtle bug or something I forgot. Maybe I accidentally used := inside an if block when I meant =. Or maybe I set an index variable in a range loop and then forgot about the code where I meant to use it. And my finely honed toolset for beating the compiler into submission sat unused. Real problem after real problem fell before maps and slices.
Then I started to really understand interfaces. I came to the language thinking "Oh, so they just all objects structs and abstract objects interfaces, only they're both stupid". But I was being stupid. It took a while, but I started to see the patterns that arise from structs and interfaces. To me, they started to seem simpler and more powerful than the patterns I new from classic OO.
And the final nail in the coffin was when I realized that I almost never had trouble reading code other people had written in Go. And not just reading it, but being able to understand exactly how it worked. A sort of "nulles in verba" - I didn't have to trust that the code did what the semantics (variable and function names) indicated, I could easily trace through the static structures of the code and confirm for myself. Because it's hard to do "magic" in Go and in order to build abstractions, you have to be very explicit.
What I think Go is
I used to make this comparison between languages like Python and C# (two languages I was working in around 2012). The quality of C# code tended to be very uniform, never terrible but also never beautiful. Python tended to vary wildly. A great developer could write something beautiful in Python, but lesser developers could easily write awful code. And I believed this was probably true of all languages - that there was some optimal value for the average quality of code and the only thing that you could alter with language decisions was the standard deviation.
Go was the first language that really broke that for me (with one caveat, I can elaborate if anyone cares). Sure, a really bad developer can still write really bad code. But code written by a bad developer in Go is almost never going to be as bad as code written by the same developer in Python. And it won't be much worse than if it was written in C#. But a good developer can still write good code in Go. Almost as good as in Python and far better than C#.
Most languages I've worked feel like they were either designed from theory or glued together from feature requests. Neither, in my opinion, are great. Languages that are very theoretically pure (functional languages, C, Rust) tend to allow code that is precise but difficult to read unless you already know what it says. Languages that are glued together (C#, JavaScript, PHP) tend to end up with just a mess of ways to do something.
Go is the first language that seems to built as the solution to an optimization problem. It feels like the Go team took a massive library of real programs and came up with a language that had the RMS error across all of them where error is measured in WTF/hr when one developer is reading something another developer wrote.
This means Go makes some weird trade offs. It lacks things that you'd expect if you were putting together a list of features you wanted. But it's also isn't derived from a concise model of programming theory.
And I really want to draw attention to one particular point about this. I don't think Go is anyone's favorite language to **write**. It can be difficult, it lacks a lot of syntactic sugar we've grown accustom to.
My Opposition
I am in the camp of those mostly opposed to generics. Maybe something innovative would convince me, but dropping in C++ style generics is something I strongly oppose.
Let's start with two basic problems. First
func Sum(vals []<T>)<T>{
var sum <T>
for _,v := range vals {
sum += v
}
return sum
}
What will that code do? What if I give it []string{"not","what","you","expect"}.
The next big problem is code explosion. If I include a line like
var foo safemap<string, bloomfilter<string> >
how much code is that going to generate? Probably quite a bit.
Weak arguments
There are some arguments that don't hold any sway with me, lets get those out of the way.
Why do the language developers get generic and we don't
Because you'll abuse them, write unreadable code and then the rest of us have to clean up your mess.
Go works in practice because it's built on practice, not theory. It's a philosophically pure language. We get generics for slices and maps because that handles most of the cases we need. There isn't a generic bloomfilter because you don't need one very often and not everyone knows how they work.
I can't make a generic data structure
Yes you can, that's why we have interfaces. This is a matter of personal taste, but I've come around to liking interfaces more for this.
But I can't write a type safe generic data structure
Yes you can, take a non-type safe generic data structure and put a type safe wrapper around it. Yes, you will have to do it for each type, but if that really bothers you, you can write a code generator.
Good Arguments
Interfaces are slow
Yes, going through that pointer is a relatively expensive operation if you're doing a lot of simple ops on a lot of data. But I'm not convinced this problem justifies the inclusion of generics.
First, Go isn't **that** fast. It's fast enough for most things, but it doesn't have much in the way of optimization. If you're really worried about performance at that level, Go probably isn't the right language. And I don't think it's a good idea to fundamentally change the language to appeal to a small set of tasks the language will never be great at. At some point, I hope there will be an effort to improve the optimization and allow optimization flags that will trade compile time for run time. I think that opens the opportunity to treat interfaces like generics in some cases.
This is also another area where code generation can pick up some of the slack.
The Request For Examples
I'll finish with this. I'm not completely opposed to generics. But I'd rather not have them then have them done poorly. The core team seems to feel the same way. They've put out a request for examples. And there's already been a lot of debate about what constitutes an example of a problem. So I propose the following for those wishing to make a case for generics:
Find examples of real code in public repos that someone else wrote where you wish the author had access to generics. And show an example of what some of the code might look like if we had generics.
I think this is consistent with the two best features of Go - it keeps us focused on how Go is actually used and it provides us a way to compare the readability of code with and without generics and compare between different models for generics.
END OF DOC, possible Post
There's been a lot of talk over the last few days about generics in Go 2. I'm personally in the camp that does not support them, but I could change my mind on the matter - I did once before. I'd like to present one axiom and one suggestion for a discussion here.
The axiom: the best feature in Go is that it minimizes the WTF/hr rate when reading other peoples code. What ever changes are made to the language, this feature above all else should be preserved.
The discussion: find example in public repos of code other people wrote that you think could be improved by generics and provide some examples of what you think they would have written. This means that the style should be consistent with the original author, not how you would have written it. And it gives us a place to explore different models of how generics could be implemented in Go 2. Honestly, my bet is that none will be preferable to leaving them out, but I really am open to persuasion.
A final note: There are already several forums for discussing the merits and disadvantages of generics I don't think we need another. I'd suggest we keep this to a collection of examples and possible implementations that can be referenced in those other forums.