A Brief History of Programming

Why Functional Programming Matters

billperegoy
im-becoming-functional
7 min readJan 11, 2018

--

The Early Days of Programming
There is so much hype these days about functional programming. As you’d expect, parts of this hype is valid but in some ways the prospect of functional languages as a cure for all problems is overstated. Let’s take a brief walk through the history of programming to see how we got here and more objectively evaluate why functional programming may help solve some long-term issues.

In the early days of programming, there were no high-level languages. You had to write your code at the machine or assembly language level. This means that you needed a lot of knowledge of the underlying machine and there was no real abstraction over the work that the processor was doing. This led to a number of problems.

  • There was no separation of data and code. In fact your code could easily overwrite the program as it was executing.
  • Control structures were simple goto statements. In a complex program, it was difficult to understand how a program ended up getting where it was.
  • Data could be changed by any part of the program. When debugging a program with incorrect data, it was very difficult to figure out where that data may have been changed.

I like to summarize these three large problems with simple questions.

  1. Where is my data?
  2. How did I get here?
  3. Who changed the data?

As we walk through subsequent generations of programming languages, let’s discuss how these problems were addressed by each generation.

The First High Level Languages
The first high level languages were utilitarian languages like Fortran and COBOL. They were designed to solve math or business problems. They were a huge leap forward in that they abstracted the programmer from the machine they were writing for. Writing in a high level English-like language made it easier for programmers to focus on the problem being solved rather than the details of the computer.

Looking at our list of problems above, I’d say the biggest advance with these languages was their ability to separate data from code. With the ability to name variables and the addition of rudimentary support for arrays and records, the programmer was able to more easily see their data and visualize it in a way that made more sense in the domain of their problem.

As far as the other three problems, we still didn’t make much progress. Control structures were will largely simple comparisons and goto statements. Data was also largely global, so understanding who changed something was still a huge challenge.

So with these advances, our big three problem scorecard looks like this.

  1. Where is my data? (solved)
  2. How did I get here?
  3. Who changed the data?

Next Generation Languages
In the years following the development of these initial languages, we got a new generation of languages like Pascal, PL/I and eventually C. While there were many advances that came with these languages, In my experience, the significant of these advances were related to their more advanced control structures and use of subroutines and functions.

Instead of the basic goto-type control structures, these languages gave us more advanced for-loops, while-loops and other more expressive control structures. This meant that instead of having to trace your program execution through a spaghetti-like sequence of conditionals and goto statements, you could read the code and start to infer the intent of the programmer.

In addition, these languages moved beyond the rudimentary call and return statements of the previous generation of languages and gave us more expressive subroutines and functions as ways to execute repeated code.

These advances largely solved the problem of how did we get here. If you’re keeping score, we now have solved two of the “big three” problems.

  1. Where is my data? (solved)
  2. How did I get here? (solved)
  3. Who changed the data?

Object Oriented Programming
Even with these advances, as programs became larger, they were still incredibly difficult to debug. This was largely due to our difficulty in understanding how data was getting changed. With these procedural languages, it was still possible for any bit of code to modify virtually any data. While there was some ability to use local variables inside subroutines or functions, most data was either passed around or globally modified by multiple parts of the program. In the end we now had a spaghetti-like structure to our data that was almost as painful as the control structure problem we had solved a generation ago.

The answer to this crisis was to break our problems into smaller, self-contained “objects” and tightly control how those objects cold be modified. An object was a collection of data and a set of functions (commonly called methods) that could be used to access or change that object’s data. Data could not be changed in any other way. We now had programs made up of many smaller objects with very controlled ways of changing data. We could now know with absolute assurance who changed the data. It looks like we had reached Nirvana. If you’re keeping score, here we are.

  1. Where is my data? (solved)
  2. How did I get here? (solved)
  3. Who changed the data? (solved)

Wait, There’s More?
So now we’ve solved all the problems of the programming world. So why is there more to write about? It turns out, even after the object-oriented revolution, programs were still very difficult to debug. As the power of computers kept advancing, we came up with larger and larger programs with more and more interconnected objects. While we could now say with certainty what method may have changed some data, we still had a complex interconnected collection of objects. Each object was able to send messages to any other and cause data changes. Understanding this chain of messages and its effect on the underlying data was in many ways as difficult a problem as the previous issues. It was made even more difficult because the data was now spread out inside of thousands of small objects. So, it looks like we have a new problem we didn’t anticipate.

  1. Where is my data? (solved)
  2. How did I get here? (solved)
  3. Who changed the data? (solved)
  4. How did the data change get triggered?

Moving on to Functional Programming
While it might be possible to start solving this latest generation of problems by adding a new layer of abstraction to solve problem number four, functional programming takes a more radical approach. With functional programming paradigms, we’re asked to consider what would happen if data couldn’t change at all.

This does seem radical. If we can’t change data, how do we accomplish any work? The functional approach says that instead of changing data, we always make a copy of any data we operate on, make changes to that copy and pass that copy to other functions for further changes. This means that it is impossible for any part of our application to have data it uses unexpectedly changed. It just receives data, and passes along a modified version of the data.

This seems so radically simple so why didn’t we do this years ago? Actually languages like Lisp have been operating on this premise for years. These early functional languages didn’t see widespread usage for a number of reasons. First, the syntax of Lisp-like languages scared a lot of programers off. But more importantly, until recently much more compute power was needed to efficiently keep copies of the many changed versions of data. These problems have been largely solved and we are seeing more mainstream interest in languages like Scala, Erlang, Elixir, Clojure , Haskell and Elm.

The Future
It’s still a bit of a learning curve to jump from an object oriented language to a functional approach. Many of the techniques we’re used to using to to make our large program look like a lot of small objects need to be revisited. With functional programming, everything is just a function and any function can potentially operate on any data. We need to work up new paradigms to associate data with the functions that should be operating on it.

In addition, in object oriented programming, we are used to small objects, each with a small amount of associated data. This means that when operating on a piece of our application, we can be assured that we know exactly what data we also have access to. In a functional world, data can be much more global in nature and we need to develop a new set of paradigms to ensure that this data is managed in a sane and effective way.

These issues are all being worked on and many existing functional languages have either embedded facilities for handling these challenges or well supported libraries and frameworks for aiding in this process.

So, is functional programming the future? I’d say yes. With the advent of more multi-core programming challenges, immutable data is critical and the challenges we’ve seen in maintaining large object oriented codebases have been huge. But there’s no need to make the instant and complete leap into a new language. As you continue to write code in your current languages just be more aware of places where using immutable data and simpler function-based programming might serve you better than building bigger and deeper abstractions in your object oriented world. I have found the biggest gift my recent study of functional programming has given me is the ability to solve problems in a simpler and more explicit way regardless of the language I am coding in.

--

--

billperegoy
im-becoming-functional

Polyglot programmer exploring the possibilities of functional programming.