There are a lot of things that, upon initial investigation, turn out to be false after some deeper analysis. There are a striking number of these propositions that not not only turn out to be false, but it is the inverse that turns out to be true!
One of these is many common descriptions of pure functional programming against imperative programming. Here are some that I have seen:
- functional programming is on the other side of the fence of (or some such) imperative programming. That is, there are two (or more) very distinct, non-unifiable methods or disciplines of programming.
- Functional programming is more restrictive than imperative programming.
I hope to address the first point another time and on the second point, for now, I will assert that:
Not only is functional programming not more restrictive than imperative programming, but quite the opposite is true! Imperative programming is one specific type of functional programming that is relatively seldom worth using.
In the relatively rare (though inevitable) case of performing I/O, only then will I resort to the imperative programming technique within pure functional programming. I will reason about my imperative code as if it were pure anyway, but I also understand that there are many destructive updates that must be appealed to — for example, the many common file system types that are used.
I don’t wish to elaborate on this statement right now, since I am still deciding how I will do it and I also know how this fact can fail to be recognised by many so I want to get it right. Importantly, it sets (one of) a premise for another issue that I’d like to write about some time.
In the meantime, I want to refer to Part 2 of Simon Peyton-Jones’, “A Taste of Haskell” where he says at 1:06:42:
[Referring to the IO data type] This is imperative programming, embedded within Haskell [pure functional programming].
There it is, right there, in one sentence
How deep is your analysis, really? All you have done is traded one poor ontology for another poor ontology. Dijkstra warned us about this: Trading one conceptual breakdown for another means nothing unless it allows us to separate concerns.
First, you did not identify the original dichotomy between functional programming and imperative programming, and did not relate why the dichotomy came about. I think you don’t understand why, and assert that understanding why is critical to understanding how the terms are commonly defined.
On this matter, I’ll quote perhaps the most widely respected book on programming aesthetics, Michael Scott’s Programming Language Pragmatics: “The many existing languages can be classified into families based on their model of computation. [...] The top-level division distinguishes between the declarative languages, in which focus is on what to do, and the imperative languages, in which focus is on how the computer should do it. Declarative languages are in some sense ‘higher level’; they are more in tune with the programmer’s point of view, and less in tune with the implementor’s point of view” (Page 5). In a so-called high-level language, the programmer’s view-point and the implementor’s view-point will never be exactly the same. However, imperative languages provide strong hints about how to do something.
In truth, it is a mistake to call languages with an object-oriented model imperative and it is a mistake to believe Wikipedia when it says functional programming is in contrast to imperative programming. To illustrate my point with a trivial example, the object-oriented model generally supports reflection, which is neither declarative or imperative but instead interrogative.
However, it is also incorrect to say that imperative programming is a division of functional programming. Why would you want to compartmentalize functional programming in this way? What value does it bring other than familiarity which is always subject to opinion? Changing ontologies here does not seem beneficial to the programmer or implementor. In fact, it is just as frantic a categorization as the original ontology. I would rather model my problems at a higher level of abstraction. As Christopher Strachey once said: figure out what you want to say before you figure out how to say it. What you want to say will generally need to include some mix of knowledge to form a cohesive picture of the problem application domain. If you are building a tool that sits atop a language or some API for that language, then interrogation is quite useful.
Come to that, I have something to say about this quote: “In the relatively rare (though inevitable) case of performing I/O, only then will I resort to the imperative programming technique within pure functional programming.” My intuition says that you should resort to describing the part of your problem that requires imperative knowledge with imperative programming. I/O happens to fit this case well, and it is also an area of programming that succeeds well with programming-by-contract when it comes to writing portable code.
In conclusion, the split between functional programming and imperative programming is a false dichotomy and a frantic ontology. The current ontology is just as frantic: There is a large amount of variability in what can be considered an imperative language and what constitutes a strong hint on how to do something. For some reason, most people understand intuitively what these strong hints are although there is no precise way to define them. We could argue that allowing manipulation of memory addresses as in C is a strong hint, but how do we explain control constructs like the for statement in C as being imperative in comparison to more explicit control structures in Haskell? If there is a good explanation for this, it is beyond my reach.