I’ve spent the past two weeks getting started with ClojureScript and core.async. It’s been been an interesting experience, not only because it’s the first new programming language I’ve tackled in recent history, or even my first purely functional language, but rather because I’ve come to feel like there’s something fundamentally missing with the approach I’ve been taking.
To clarify: there’s a lot of inherent educational value in learning a language outside your comfort zone as an academic exercise — being exposed to unfamiliar programming paradigms is invaluable — but I’m going to be mostly talking about adopting a new language as a practical tool for building things.
We have the tools
It’s easy to learn the syntax of a new language. These days you can find a wealth of good reference materials online for pretty much any major language, and there are plenty of ways to get your feet wet building simple “Hello World” apps or working through the Project Euler problem set. I’ve been impressed by the elegance and expressiveness of the ClojureScript programs I’ve written, but they’ve all been on the order of a couple hundred lines of code; it’s not hard to make any language appear simple and elegant in such a contrived situation.
It’s also easy to understand on a philosophical level why you would want to use a given programming language or tool, especially for a language with a lineage like Clojure/ClojureScript. Every time I watch one of Rich Hickey’s talks about why he built Clojure, I feel like he’s voicing a deep truth about software development that I’d never before been able to verbalize.
The missing step
From the standpoint of building software in industry (as opposed to, say, PL theory), a programming language is essentially a tool to help developers deliver business value. From that angle, a well-designed programming language is one that minimizes the cost of change over time while encouraging developers to make architectural decisions that also minimize the cost of change.
There’s no objective measure of ‘goodness’ here, only trade-offs. Dynamic languages like Ruby enable kinds of metaprogramming that can be incredibly expressive, for example, but that comes at the expense of requiring more intensive testing to safeguard against the sort of mistakes that would be trivial to catch in a language with a stronger type system.
I don’t have a good answer yet, but I’m left with a vague sense that there has to be a better way.