Return of an old solution to address new problems
Prepare for the rise of Functional Programming, a guide for non-mathematical programmers.
Over 35 years of developing software I have been witness to the development and rise of imperative programming languages. In the beginning it was a very mixed-up and undisciplined world. The lack of control structures and the reliance on line numbers for flow control (GOTO and GO SUB statements) resulted in programs rapidly degenerating into a pile of un-maintainable “spaghetti code”. This problem will be well known by those of us who grew up in the era of Home (micro-) computers, in 1980’s UK, such as the Sinclair ZX-81/Spectrum, Commodore 64 and the BBC Micro. One notable computer of the time was the Jupiter ACE, now lost forever, which used the Forth programming language rather than BAISC; a computer ahead of its time as we shall see.
Languages like Pascal, Structured BASIC and C returned order to the chaos, and fostered the development of Procedural programming. This paradigm brought a more controlled style of coding to bear. This encouraged more up-front consideration of the problem and formulation of the candidate solution in a task called Design. There were several ways of conducting design, usually through functional decomposition adopting a top-down (breaking a big problem down into smaller, more manageable pieces) or bottom-up (composing a large body of functionality from smaller pieces) approach.
The rise of Object-Orientated Programming (OOP)
Moore’s law (Gordon E. Moore, the co-founder of Intel, 1965) has been with us now for 50 years and until recently (last ten years) has been a reasonable predictor of the growth in computer power. However, whilst the integration of transistors has continued, largely, on its predicted trajectory, computing power has been forced into employing some problematic techniques in order to try and keep pace.
Over the last 10 years processor clock speeds have stalled and in some cases reduced. In “compensation” we have multi-core processors to spread the load but here lies the issue. How do we develop code to take full advantage of the multi-core hardware architecture of modern (and the foreseeable future) computers.
According to commentators like Uncle Bob (Robert C Martin): our idea of parallel coding in imperative languages is based on threads that are managed by the operating system. However, this approach is flawed because it relies on all the threads of a single program executing on a single processor (core). We cannot rely on the OS to facilitate parallel code execution over multiple processor cores, largely because of the inter-dependent units of code (classes, methods and functions) exhibit when employing imperative programming languages and especially when using OO. The crux of the issue is maintenance of state information, which is a key feature of imperative languages. It is virtually impossible to maintain reliable state over multiple processor cores, so we need another paradigm.
Is FP really so different from OOP?
Back in 1982, the UK computer company Jupiter Cantab released the Jupiter ACE (mentioned earlier). Unlike almost all its contemporaries, rather than the BASIC programming language, the ACE used a variety of Forth based on Forth-79. Ace Forth was rather odd and more difficult for novice coders to learn but it had distinct advantages over unstructured BASIC; although is still an imperative programming language. Forth did not rely on line numbers but used labels to change flow control. One of the more difficult concepts it employed though was the Reverse-Polish notation (RPN); indicative of one of its influencing languages Lisp. Two perform a simple addition of two numbers in BASIC one might state something like:
LET A = 1 + 2
In RPN the instruction does away with the equals sign and would look more like:
1 2 +
RPN uses the stack to maintain the result of the calculation, which can be referenced as “.”. Lisp uses Polish Notation (known as S-expressions) to archive the same thing. The operator (or function) precedes any operands (or parameters) in an approach that is based around list processing.
(+ 1 2)
Incidentally, the roots of the BASIC programming language include ALGOL-60, which went on to influence the Pascal, C and Simula (recognised as the first OOP) programming languages.
So, what is so special about Lisp? It’s not an Imperative but Functional programming (FP) language. It does not attempt to deliver functionality through the maintenance of state but through a more algorithmic (mathematical) approach. It is at this point most articles I have read descend into mathematics and topics like lambda calculus and category theory, but as I do not understand these you can rest assured.
Functional Programming adopts more of an algorithmic style but one complication is the lack of “immutable” variables. As in mathematics, after assigning a value to a variable (poor name), Purely Functional programming languages do not allow re-assignment as this is in effect attributing state and can result in “side-effects”. Side Effects are the unpredictable results that are produced when state is allowed to change with (or maintained by) a system.
How pure do you dare go?
However, this is not the end of the story. Purely Functional programming languages are incapable of resolving real-world problems as many require features such as data persistence, user interaction and system-to-system interfacing; all of which represent mutable data sources/sinks. But there is a solution (or fudge if you like), known as Monads.
It is well recognised that Monads appear to have a strange power. As soon as you realise what they are and how they work (at least in the mathematical context), they render you completely incapable of explaining them to anyone else. Luckily, I remain unaffected – I do not claim to have an understanding of them but have formed the opinion, which may renounce at some time, they are a bodge. They are a way to make FP languages practicable by isolating the impure elements.
So, what’s so good about FP. Programs implemented using a FP language ensure modules are self-contained and able to operate consistently (independent of when they are called) and independently (without dependence). Consequently they can be executed not only in different threads but on completely different processor cores. By isolating the impure elements in Monads (i think that’s what they do) the unreliable elements can also be kept self contained.
There are other problems FP can help with such as the processing of Big Data. The term Big Data remains ambiguous with different commentators applying different interpretations but the one I prefer best illustrates the problem FP could fix.
Big Data is represented by a data store of such volume and/or complexity is becomes impractical/impossible to relocate it to where it can be processed. Indeed, processing of the dataset needs to be performed using a parallel, rather than a sequential/serial methodology. In essence the processing rules (compute) has to be distributed about the data store and the results returned and collated. The nature of FP lends itself well to distributed processing so could present considerable advantages over Imperative techniques.
Have I pricked your interest in Functional Programming?
For Java developers there are a couple of options: Scala and Clojure both produce JVM object code. Scala is more a hybrid language that should provide a smoother path of transition from OOP to FP, whilst Clojure is ‘purer’ (arguably).
In the Microsoft camp, F# is also multi-paradigm, has been around a while and is growing in popularity, and Linq employs some FP techniques. Clojure also compiles to CLR (Common Language Runtime) object code so you could edge your bets and become truly platform agnostic.
If you are really feeling brave you could always try some of the more formal FP languages such as Haskell, Erlang (that was used to create WhatsApp), or you could go real “old school” with Common Lisp or Scheme.
Are we ready and willing to adopt Functional Programming?
My biggest concern, relating to FP, is the ability and willingness of the developer community to adopt the paradigm. If projections of the size of the coding community are to be believed (doubling every 5 years) there could be as little as 6% of the developer community that have any experience outside of OOP, and even fewer with any FP background at all. I suspect that many formally (university degree) trained Software Engineers over the past 20 years would bulk at the mathematical nature of FP and would rather retain their OO comfort-blanket. It is likely to be even more the case with Microsoft and Java certified developers – and who could blame them?