I’m not interested in programming languages. There, I said it. That would probably be a fairly understandable statement for pretty much any adult in the 21st century, but it’s particularly unusual and maybe even heretical for someone who spends most of their life as a software developer.
I’m not interested in programming languages, but I’m captivated by what I can do with them. To me there is a huge difference. A language is a tool, a method of expression that enables me to convene with Silicon in the same way that the English language allows me to convene with Carbon. I use computers to entertain me, to educate me, to challenge me, to communicate with those I care about, and to manage pretty much everything I do. A communication channel that helps me to realise those desires as effectively and naturally as possible is like the sculptor’s chisel or the painter’s brush. I’m sure some painters really care about the specific chemical composition of the paints on their canvas. But for the majority, I suspect, what matters to them most is that the tools they use allow them to portray as effectively as possible the emotions in their minds. And if that vivid blue strikingly conveys the depth in a young girl’s eyes, or the raging of a tumultuous ocean then it doesn’t matter how much Cobalt it has in it.
That’s how I feel about programming languages. The seemingly incessant desire shared by a large fraction of the developer community to use the latest and shiniest fad language has always perplexed me. And the subconscious assumption that most senior developers hold, that a programmer should know every inch of their language of choice, including those features that one should never use, seems counter-productive at best, and at worst actively filters out the people you should be looking to employ. Perhaps it’s because I use a small number of languages, and I always have done. It’s not that I never use new languages – I’ve probably used a dozen new languages in my current job – but whenever I’m looking at a new, large-scale project, I always go back to the same one or two core languages with which I develop anything of import. I’m not aware of anything I need to do that can’t be done with a reasonably small subset of my language of choice, which happens to be C#.
Functional languages perplex me even more. I remember back to my earliest undergraduate days when I was taught ML as part of the first year computer science course. I never really understood it. It was an interesting intellectual curiosity, perhaps even a vaguely entertaining mental challenge, but I never considered the possibility that it could be anything more than that. Which is one of the reasons why I was so surprised, a decade or so later, to discover it being used enthusiastically in commercial situations. Usually, it has to be said, by people who had moved into industry after doing postgraduate research in computer science. Of course, I myself am somebody who has done post-doctoral research in computer science, yet one of the reasons why I’m not doing it any more was that I found a substantial fraction of the research done, especially on the theoretical side, was perplexingly irrelevant intellectual self-gratification with no plausible real-world application. Functional languages largely, in my opinion, fall into that same category. I just don’t get the appeal and I never have.
If I listed the most important goals of any software development process I suspect I would align fairly well with most senior developers. Firstly, the application should do what it’s required to do. Secondly, it should be as simple as possible to maintain. Thirdly, it should be as cheap as possible to build. After that you can start looking at aesthetics. The reason I put 2 and 3 in that order is because the vast majority of the lifetime of any software application – and therefore the vast majority of the cost allocated – is spent on the post-launch maintenance phase. Support, training, bug-fixes, patches, adding new functionality, reworking existing functionality, improving performance and stability – that type of thing. I suspect a great deal of the differences of opinion between me and my colleagues might actually come down to how we order (2) and (3) on that list.
As far as producing features is concerned, I don’t have any particular doubt that functional languages could be made to produce the same level of complexity that is achieved by mainline imperative languages. So I’m not really arguing over point (1) – I’m arguing over (2) and (3). In my experience, functional languages have a vastly steeper learning curve, produce code that is much harder to understand at anything other than the lowest level, and is much harder to reason about in any kind of intuitive “human” way.
My view is that programming should be done in a “narrative” style. It’s one of the reasons why I so strongly oppose abstract frameworks like Spring – they encourage code to be written in a way that actively opposes the way the human brain naturally functions. When I write code, I should be able to interrogate it like I would interrogate a human being performing a task. I should be able to follow allong the chain of logic, the sequence of tasks and contingencies, and I should understand the process from a high level with relatively little mental input. That’s how my brain has evolved to think, so to write code in any other way is obviously not going to feel as natural to me and is therefore going to require more effort without any commensurate benefit.
Functional programming opposes the way the human brain naturally works in several ways. Most importantly, by avoiding mutable variables. Mutable variables are the computational analogue of the things in our lives that change. Like, oh, everything. You and I are mutable variables. When we change our minds we are not replaced with near-identical copies of ourselves. I can see that there are some situations where this kind of behaviour might be useful – for example, in dealing with mathematical equations – but not in a large scale system. When I drive down my road, my car is changing its location at every point. This document is changing every time I add a new letter to the sentence I’m typing. Everything in the world changes, and tracking those changes is what our brain has evolved to do. So forbidding a programming language from working in this way seems (obviously, to me at least) to be setting ourselves up for additional complexity that we shouldn’t have to face.
Functional languages insist that we think from the goal backwards, which is never how humans plan tasks in our real lives. Think about it – if you asked directions to my house, I wouldn’t reply with “Once you get to my front door you’re done. To get to my front door you have to walk up my drive. To get to the front of my drive, you have to walk from the bus stop 100m up the road in a north-easterly direction. To get to the bus stop, step off the bus. To step off the bus, move to the front and wait until it stops. To get the bus to stop, press the red button…” Why don’t we speak like that? Because that’s not how our brains work. Our brains work in a narrative sense. My directions from your house to mine will start at your house and talk you through the journey sequentially. Our brains remember stories, not abstract, chronologically isolated instructions.
I’ve been in this argument a thousand times. “Ah,” begins the functional programmer, “but that’s just because you haven’t been writing code in a functional language for long enough. If you spent more time doing it then you would learn to think that way”. Perhaps. Perhaps not. I have spent a long time writing in a functional languages and though I understand the concept, it was never natural for me. Maybe for some people it is, but I suspect those people are in a minority. And it’s also largely irrelevant – if it takes a long time to mould your brain into the shape required to think in a certain way, then that way of thinking had better come with some pretty amazing benefits or else I’ll say it’s a waste of my time. And my brain.