Golang Generics

From Adam Colton
Jump to navigation Jump to search

Title: Generic: opposition and proposal

I quit Go over the lack of generics once. 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 compilation 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 OOP.

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.

My Opposition

I am in the camp of those mostly opposed to generics, but more specifically I'm opposed to C++ templates.

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"}.

Also consider the error handling. If you import a package and use a function that takes an interface but fail to fulfill the interface, you get a fairly clean error message. And most importantly, it points to an error in your code, not the imported package. With templates, if you fail to fulfill the expectations of T (in the case above, having +=), you'll get an error when the package compiles.

Performance

One of the concerns that templates address is performance. Code using templates will generally be faster than code using interfaces.

However, if we look at this via O notation, the story is a little different. Templates will slow down the compiler. For a fixed set of functions, interfaces will compile at O(1). Templates, using that same fixed set of functions but with N types invoked upon them will compile at O(N). And despite the fact that interfaces will be slower at runtime, they are proportionally slower, both running at O(1). That may seem like an academic argument, but there's a practical, cost consideration to it. Developer time is expensive, machine time is cheap. You'll pay that compiler cost mostly in developer and but you'll pay the running cost in machine time.

Also, 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.

Type Safety

The strongest and most cited argument for generics is type safe containers. I certainly agree with the importance of this, but not to the extent that I would support templates.