Tools, Fools, and Types

I once knew a skilled carpenter. He had a saying, about screw drivers and drills and such:

It’s not the tool, it’s the fool.

What he meant is that a master carpenter with poor quality tools is better than a novice carpenter with high quality tools. His particular example was to say “A master mechanic can take apart and repair the entire engine room on a ship with a flathead screwdriver and an adjustable crescent wrench. If you’re blaming your tools, you should be blaming the fool who uses them instead.”

I have been thinking a lot lately about programming languages because there are so dang many of them, and I can’t possibly learn them all. I’ve been to meetups recently focused on Rust, focused on F#, focused on JavaScript. My career is a time-bound operation. I have finite waking hours. (Finite keystrokes). So I want to learn primarily the best languages, the best tools, but I am not sure what they are. Not that I think there will be a silver bullet, a programming language that makes everything better, but that I want to be a little bit less foolish about what code I write.

The other day I was at a Meetup for the Rust programming language. It was by the founders of a Rust-focused tech consulting start up. I really enjoyed it and I learned a lot, but it got me thinking about what Uncle Bob has called the Type Wars.

Briefly stated, the type wars are an argument between people who think that a strict language (in the informal sense of the word, just a language with rigid rules of some kind) will help you be more productive than a loose language because there are certain types of bad programs it won’t let you write. On the other side, people argue that a loose language will let you write faster, and then you just write tests to prove your program is correct, so all the rules in the strict language will just slow you down. We could list F#, Haskell, and Rust as being firmly in the strict camp, while we could list Ruby and JavaScript on the loose side.

Mark Seemann wrote a fantastic example of the strict language argument. He essentially argues that, out of the set of all programs, most are very bad and obviously wrong. Therefore, languages that have strict rules produce better programs because fewer of the really bad programs are allowed.

On the other side, we have The Shocking Secret About Static Types by Eric Elliot. The article talks about the recent popularity of TypeScript, a language that lets you annotate JavaScript with more type information (that is, it lets you write JavaSript, a very loose language, in a way that is more rigid.) The article basically argues that, although using compile-time types does make it harder to make certain errors, it doesn’t reduce bug count, and that what we should really do to reduce bug count is having better unit tests. Elliot’s main source seems to be a paper in which the authors looked at the top 17 programming languages on GitHub, and looked at the 50 most popular projects in each language, to try to discover whether popular, well-maintained projects in one language are measurably better than those in other languages. That article is an interesting read, but I am always dubious articles that rely entirely on statistics about some set of human activity, simply because the assumptions that went into gathering the statistics might be flawed, and the assumptions that went into interpreting the statistics might be flawed, and the errors in these assumptions multiply one another, so the article could be way off. Perhaps I’ll write more on that later.

Anyway, this got me thinking:

Can I make an analogy between writing programs and searching for something in a finite space? In other words, if we do a thought experiment where the programmer is searching for the right program in the set of all programs that his language will allow, can we gain any useful insight into programming language design? I like this approach because speeding up a search algorithm is much easier to reason about than the entire process of creating software is, and the analogy is not that terrible; programmers often consider and reject many alternatives before they are happy with a piece of code, and that is a little bit like a search algorithm.

Mark Seemann and others want to optimize the search for good programs by shrinking the search space. In other words, they argue that if there are fewer valid but flawed programs, the search for valid, non-flawed programs will go faster. Based on our analogy, they’re right. If you’re looking for a specific book somewhere in the library, good luck. But if you know anything about the book, the author or the call number the library gave it, you might be able to find it quickly. Shrinking the legal search space is a smart optimization for search algorithms.

What about JavaScript and Ruby? You can do all kinds of things in JavaScript that will deterministically fail at runtime, and nothing will stop you. No compiler will say, “Wait, that doesn’t make any sense! Error!”

So for JavaScript, the search space is huge. JavaScript doesn’t have any of the rules that stricter languages have that would prevent you from writing obviously senseless programs. So do the rigid languages win this round?

I’m not sure. In the rigid languages, the search space is smaller, but the search is slower. In JavaScript, the time from having an idea to having a testable little chunk of code might be briefer. So although the space the programmer has to search through is larger, although there are more possible programs to consider, the developer won’t have to consider each of them for quite as long.

If we really push this analogy, the question becomes this: Does the shrinking of the search space help more than the adding of picky rules that slow one down hurts? I don’t know the answer to that question, but it is definitely worth thinking about.

My personal preference is usually for the rigid language. Eric Lippert says that one benefit of these rigid languages (my term) is that the language

“… is a tool for producing a partial proof that the program is correct, or at least not broken in some obvious, detectable way.”

I find this partial proof comforting. It makes the number of things I have to worry about smaller.

I personally think, also, that a specific subset of the rigid languages, namely the functional languages, have a distinct advantage here: They are better at dealing with concurrency.

Basically, concurrency is the set of programming problems that arise when more than one process is acting at the same time. Because functions’ outputs depend only on their inputs, and not on the outside state of the world, they are less risky to use in situations where many processes are acting than other constructs are. I think we might reach the point where functional programs, which are much less reliant on storing the state of some process than other programs, are just better for dealing with the kinds of problems we have.

Still, I think the size and space of the search space depends more on the problem domain than on anything else. Perhaps the type wars end in stalemate, and it’s really about the fools writing the code.

Till next week, happy learning!


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s