Functional purity vs physical time

What is functional programming?

The most well-known definition of FP is based around the concept of “referential transparency”: a codebase is considered “functional” if all expressions in that codebase are referentially transparent. Simply speaking, an expression is referentially transparent if replacing it with its result in the program does not alter the behavior of the program in any meaningful way.

This definition is certainly precise, and more importantly, useful, in that it can be readily used by a programmer to check whether a piece of code is functional or not. Arguably, however, it lacks a strong philosophical backing: it doesn’t necessarily tell us why this style of programming (i.e. maintaining referential transparency) is ultimately superior. It may even sound like a random rule to someone who has not experienced the benefits of FP first-hand.

This is where I think my definition may be useful:

Functional programming is simply preventing the details of our physical world, specifically time, from leaking into our programs.

Physical world vs mathematical definitions

In our real, physical world, time is arguably the most essential concept, as nothing can work without it. Any change or action in the world happens through time. In one moment, the world is in state A, and in the next (as a result of an action) it ends up in state B. The fact that the speed of light (speed of information transfer) is capped makes time even more essential: time needs to pass for information to pass along.

It is no surprise, then, that even our computers have been built to operate based on time. E.g. a register in your CPU holds different values at different times.

It is, therefore, also not surprising that most of the initial programming languages, which, in order to maximize performance closely mimicked the hardware on which they ran, also operated based on the same concept of physical time. I.e. they were imperative: a program would read like “in order to sort a list, do this, *THEN* do that, *THEN* do that other thing, …“.

We humans are dropped into the world, where things like time and space and atoms and gravity simply “exist”, even though we don’t fully understand what they are. They are fuzzy concepts that we are forced to live with.

In mathematics, on the other hand, things are totally different. Nothing exists without being explicitly defined! Not even time! Need numbers? Explicitly define them! Need operations on those numbers? Explicitly and precisely define them! Need time? Sorry, but you’ll have to explicitly define it, like everything else!

And this is arguably where all the power of maths comes from: since we precisely define every concept that we may need (for a given problem domain), we can actually prove things, reason well, and confidently build concepts on top of concepts (e.g. calculus or geometry on top of numbers) without going crazy, or introducing “bugs”. The concepts are simply not “leaky”, so they compose easily.

This power is obviously sought after in modern, complex programming. Functional programming is simply mathematical programming, and aims to enjoy the same benefits that mathematical definitions enjoy.

Functional vs Imperative

Imperative programs are philosophically problematic because they unnecessarily and universally permeate the concept of physical time with program logic, even though 99% of these programs have nothing to do with the concept of time. E.g. sorting a list has nothing to do with physical time.

As an example, consider the (imperative) concept of variables, that is, “names whose values change”. If our program says x = 12, and after a few lines it says x = 5, then we are admitting that time exists outside of our program, and we, as the programmer, are happy to abuse time as a programming language construct.

Side effects, similarly, are based on admitting physical time. E.g. printing a line of text on the console is only meaningful and worthwhile if there was a previous point in time when that text wasn’t visible on the terminal.

This is philosophically problematic because time is simply a random detail of our physical world and there is no good reason to implicitly fuse it into our program logic everywhere. To program functionally is essentially to come to this realization.

Side note: In FP, obviously, we will still need to change state, or carry out effects, but we are explicit about it, and we define these actions precisely, rather than borrowing them implicitly from the programmer’s physical world.