One of the supposed advantages of the functional programming paradigm is that it avoids side-effects. By not having any mutable state, all side-effects are avoided.
Well that’s an interesting property for a programming language. But is it a good property? Is it an important property?
One of the advantages of having been in this field for so long (so, so long) is that one can remember the origin and context of what is now considered common wisdom or “best practices” (more on this misnomer in a different post). I now dust off a few memory neurons to shed some light on the issue of side-effects…
Side-effects have long been considered a rich source of programming errors, starting (at least) as early as 1985. But here’s the catch — it was non-localized side-effects that were the problem, not well-localized side-effects. In common programming languages of the time that allowed global variables (C and C++ mostly), setting the value of a global from within an unrelated function was indeed a major source of errors.
But was the source of the errors the fact that a state-change was allowed or the fact that it was non-local state change, one that a programmer wouldn’t be able to predict or maybe even know about (especially given the state of the art in IDE and analytic tools at the time)? I recall that it was the latter. That the problem was with programs that had gotten so large that there were huge portions of it that any individual developer wouldn’t know anything about. For one sub-system to reach into the global namespace and tweak the behavior of another in an unstructured way led, inevitably, to huge confusion.
However, so did unprotected data structures (e.g. the C struct), and for the same reason. The danger of non-local state changes drove the movement to abstract data types, encapsulation, namespace partitioning, etc.; all these capabilities we take for granted in many modern languages. The ability to change state is not equivalent to the notion of side-effects.
So the question is, are non-mutable data structures necessary to avoid the recognized evils of non-local side-effects? I don’t think so. The OO languages that support protection on data access seem to provide plenty of options in that regard. Side-effects are, in essence, simply a poor design choice.
I don’t think this is a knock against functional languages at all — they use a different computational model that is appropriate in some scenarios and not in others. But the implicit claim that all state-changing is evil in aficianado’s rhetoric is incorrect.