I often think of why I feel that it’s important to have a functional language—not just write code that’s pseudo-functional in an imperative language. As I think this through, I wanted to record some of my ideas on the subject.
Even the best developers I’ve known are almost always inclined to take the defaults. I mean if the default string type is ASCII then you’re very unlikely to see Unicode strings in the app unless someone specifies that this is needed. This is not laziness; it’s simply that these developers have other issues to concentrate upon.
Why is this important? Simply this: I’ve come to realize that default mutability is an accidental complexity–in the sense that Fred Brooks used that term in his “No Silver Bullet” essay. Don’t misunderstand what I’m saying–mutability is necessary and appropriate. But having all values in software mutable by default is an invitation to lots of unexpected side effects. And this is why I really believe that functional languages will continue to gain in use. Functional languages are immutable by default. Impure functional languages (OCaml, F#, Clojure, and Scala among others) do allow a programmer to specify mutable quantities where it’s appropriate due to performance considerations but they default to quantities being immutable–and that’s why they’re important. If things default to being immutable, developers will learn to work with immutable quantities and become accustomed to them in the same way they’re now accustomed to thinking in Objects. People seem to forget that 30 years ago, OO was as much an academic discipline as Functional is now.
Every developer knows they should mark quantities as constant (or final or readonly or whichever keyword expresses immutability) but most do this only as an afterthought and they do it incompletely. Default immutability could eliminate a whole class of side-effect errors and therefore reduce errors in our code by a large fraction of all errors present. The issue is not the question of does “const correctness” (for lack of a better way of phrasing it) make software less error-prone; the issue is getting developers who are always under impossible deadlines to use constants by default. Languages that default to constant, immutable quantities are a large step in that direction. This is why functional programming is important and this is why you’re going to inevitably see more and more developers gravitating toward these functional languages.
I think there are two different ways that knowledge of functional languages can influence one’s programming style in imperative languages: the blatant way and the subtle way. The blatant way is to try to force functional programming concepts onto the language – – “hey, look, I have a left-fold function in C!” The more subtle, and more useful, way is to let C be C while being mindful of the lessons of functional programming: not all functions need to have side effects; often it’s best to partition off the parts of the code that do have side effects; and it’s usually a good idea to explicitly think about and explain (in comments) what the side effects are. This way functional programming expertise can influence one’s imperative programming style without it being a party trick of “doing functional programming in C”.
That’s an excellent observation. As I say, I’ve got nothing against mutation per se; I only have a problem with it being the default.
I think the main thing I’ve observed is this: good programmers don’t usually need to be told to minimize side effects and poor programmers won’t listen when they are told. So making immutability a default is, sadly, a defense against the majority of less-than-superstar programmers.
The problem goes even deeper than that. In C++ there is no way to controll mutability. Const is shallow: you may receive a const object with non-const internal pointers. This is even true with Scala ‘immutable’. More importantly, const is not the same as immutable, especially in a multithreaded environment. While you are operating on a const object (reading its fields), another thread might mutate them, because it has a non-const alias to it. Essentially, you can’t trust any reference in C++, be it const or not. In order to establish a controlled environment you’d have to study the pedigree of every argument that’s passed to you. I don’t think it’s feasible outside of toy examples.