Generics, languages and tourists
By Bjørn Borud
When whatever language I am using as my main workhorse language at the time gets new features I worry. Go is getting generics and I have to admit I’m a bit worried. I think it’ll be fine, but there is always the risk that this might attract a different crowd to Go.
Programming languages depend on (at least) two factors to be pleasant. First the language itself, and second, the people who use it. Especially the people who make libraries and frameworks. Usually the language is somewhat less important than the people who use it.
Workhorse languages
I’m a relatively conservative developer. I choose a workhorse language, invest in learning it and then try to stick to it for as long as it makes sense. Until there are good reasons to stop using the language or when something worth the switch comes along. Typically I stick to a workhorse language for the better part of a decade.
I use, and learn, other languages as well, but I always have a workhorse language that I do most of my work in. This is a good practice.
My interest in programming languages tends to be purely in terms of what kind of work I can get done in them. You can learn to like most languages. Especially if they make you productive. And the longer you stick to something, the better you become at it. It takes years to become sufficiently proficient in a language to the point where you have the ballast needed to focus on arhictecture and implementation strategies.
If someone tells you that you can become proficient in a language in just a year or less, they are wrong. Usually because they have never been proficient in a language or they have never worked in a competent environment.
When choosing a workhorse language I have a short list of absolute requirements.
Strongly typed and compiled
There is no actual advantage in using dynamic languages. The advantages people list are mostly liabilities. And they have a nasty tendency to manifest when you have lost the context you had in your head while writing the code.
The first requirement of a program is to be correct. If it isn’t correct, the rest doesn’t really matter. Strict type enforcement and good compilers can do a lot of heavy lifting for you in this regard. It is poor professional judgement to not accept that help.
If you think compiled languages are a lot of work: you have to practice more to become efficient. Learn how to do things right efficiently. Nobody cares what you can do wrong quickly because it doesn’t have actual value to an employer.
Sufficiently mainstream
If you are going to invest in a programming language you want to invest in something that has the largest possible ecosystem. If you choose a niche language there will be fewer books, videos, conferences, libraries, and fewer developers at a sufficiently senior level. This is not a good investment.
The argument that people who choose niche languages are smarter is perhaps true. I haven’t checked if there is credible research on this. But it is also irrelevant. Most of the profitable software industry is about getting things done. And most programmers are not very smart.
If the mainstream requirement rubs you the wrong way, perhaps you should investigate if your reasoning is rational or emotional.
It must have a decent standard library
A decent standard library means that you will be less dependent on third party libraries. As someone recently pointed out, much of the commercial software space is propped up by a few libraries that have unsatisfactory support. Like one person developing something in their spare time. Every third party library carries some risk and adds to your burden.
It is good engineering to try to get things done with what the standard library offers. Don’t add dependencies to something just because it is a little better than what the standard library does. It has to be able to offer a significant advantage to be worth it.
Good standard libraries are a relatively new thing. Java was probably the first language I can think of that had a decent standard library. I think Go has an even better one since it has a more pragmatic focus.
C++ is an example of a long and painful journey towards something looking like a decent standard library, but at this point, who even cares anymore.
Remember: I’m a pragmatist. I care about what you can get done. I’m sure lots of languages have far more “beautiful” standard libraries you find elegant, but I still need to get things done.
Community must focus on quality over novelty
This is similar but separate from the requirement that the language must be mainstream. A good language community is focused on getting things done and growing a stable body of work and knowledge that you can build on. If things keep changing all the time you cannot make progress. You do not want to invest in an environment that pushes you to start over every 6 months. Occasional rewrites are good - constant need to rewrite is pathological.
Yes novelty is exciting, but all change has cost and you have to pay off that debt at some point.
Java
I used to use Java as my workhorse language between 2003 and 2015. I had been using it before then, but with version 1.4, at least the promise of asynchronous networking made it a viable option for my type of work. Before that I had spent a lot of time writing highly asynchronous code for a decade in C and occasionally C++.
The way I used Java was, as you might expect, conservative. I avoided large frameworks completely, made heavy use of the standard library, and made judicious use of third party libraries.
Of course, over time the transitive dependency graphs of my projects grew beyond all belief as my projects accumulated direct dependencies that sometimes had unreasonably many dependencies themselves. Not only did this lead to applications that had embarrassingly large dependency graphs, due to lack of versioning in Java namespaces, every single non-trivial Java project would have at least one instance (usually more) of having different versions of the same library in the dependency graph. If you think this was not so, you probably didn’t know your own project all that well.
This was kind of the dirty secret of Java: every single large application was a ticking time bomb. And probably still is. We just didn’t want to think about it.
Enter generics
When generics entered Java I was at first elated because it offered solutions to problems I had. It made basic tools, such as collections, much more convenient.
Then the language geeks started to turn up. Or worse yet, the subset of language geek obsessed with using the type system to solve problems that could be solved in ways that were far easier to understand. This is when I started to dislike working with people who are obsessed with language features.
If you write software that matters, maintainability is important. The use of clever type-system tricks that require a lot of time to understand are not conducive to maintainability. To the point where it becomes an unprofessional indulgence.
I am not going to name the project (although it deserves to be named and the authors to be shamed), but years ago I had to deal with a certain class of problem where there was a framework everyone kept recommending. Presumably without ever having used it themselves. The framework was a generics bonanza extraordinaire. There was hardly any type that wasn’t genericized, and there were layers of factory-factories to manage the madness.
The “idiomatic” instantiation of a core entity was a 20-line disaster. Exactly nothing inside the finished instance contained anything that could not have been accomplished by calling new Foo()
.
And this wasn’t the only example. I also remember friends writing code where they got so lost in their complex genericized types, they had to start over. You’d point at a type in their code and ask how this was supposed to work and they’d have to get back to you.
Sidebar: Avoid language tourists
In the introduction I mentioned how important people are for a given language. One kind of programmer I’ve learned to be wary of is the language tourist – that is, novelty-seekers, often with some fetish for a certain class of syntax or idea who rarely stick to any one language for more than a couple of years.
If you can’t stick to something over time, you aren’t going to be good at it. Language tourists rarely produce enduring value. In fact, I made a survey of people I’ve worked with who fall into this category, and I couldn’t find a single project where they ended up making lasting contributions.
They expelled a lot of hot air while not getting work done though.
These tend to be the squeaky wheel on a team, always complaining about how bad the languages they have to use are, and how much better it would be if we could just use their current non-mainstream infatuation. Give them what they want and they’ll be happy for a little while. Before finding some new shiny thing to move on to while their old, unmaintained code gets binned and rewritten.
I’m an old fart and I tend to be direct. So I’ll be direct: this is a personal and professional maturity issue. It can be indicative of character flaws. That is to say: it may not be easy for a manager, employer or customer to address.
People tend to grow out of it, but you shouldn’t feel obliged to pay for their lack of maturity. If a programmer can’t stick with something long enough to develop some proficiency, and to be a team resource, they aren’t worth the investment. Let someone else take the risk and the cost.
You can sometimes find evidence of language tourism in projects. If someone describes a project as “polyglot”, chances are that you are either looking at a project done by amateurs who cobble things together in whatever languages the team members could express themselves in, or the aftermath of language tourism. The difference is usually that the latter can be a lot harder to decipher and fix. Both of these tend to have the same root cause (weak technical management), and the same endgame (rewrite or abandonment).
Generics in Go
I have yet to make use of the new generics features in Go. So far I have just looked at examples and read a few discussions. I expect that this will play out in much the same way as when Java got generics. A few years of flailing about, with some people going off the deep end and making a mess of things, before things start to settle down and we start to understand how to do things.
I think that Go can benefit from generics. Looking at my own code I do type assertions on average once every 300 or so lines of code. Not frequent, but frequently enough for me to feel slight discomfort. It helps that Go has a switch
statement that can deal with certain common patterns (though I don’t see people use it all that often).
I also think that most people assume reflection is more expensive than it really is in Go, but that’s a separate issue. (I don’t think reflection is really what’s affects performance but rather the branches it introduces in the code which can cause mis-predicts. But this is a guess.)
The thing is that even if these instances are not terribly frequent, they do tend to occur where they make themselves felt.
If you are a Go programmer, please don’t make the language designers regret introducing generics.
And if you are a language tourist, please go ruin someone else’s day. There are languages far more exciting than Go where you can be a self-pleasuring hipster bore. We’re busy getting stuff done over here.