Functional Programming

Functional programming languages, whether in Java, F#, or Clojure, share several common features. Functional programming is based off lambda calculus by Alonzo Church. The rules of lambda calculus have been applied in making languages more functional. The three lambda calculus rules that influence functional programming are:

  1. Functions are like mathematical functions. One-and-only-one return exists for each function argument or arguments.
  2. Variables are immutable. They don’t vary.
  3. Functions and variables are inter-changable.

This post will go over some of the common features that result from following these rules.

Functional languages present functions as values, and make those functions like mathematical functions that have exactly one result given the input value or values. There is no external mutation that causes functions to return different values when the same values are passed in as arguments. Just making computer programming languages match the behavior of mathematical functions results in a whole set of features normally not available in non-functional languages.

First and most important, functional functions and functional methods are thread safe. That is the main reason functional programming has become trendy after nearly 100 years of research. The killer application of functional programming is concurrent programming. And now that there is no way to speed up programs without making them more concurrent, functional programming has finally found its niche. It’s an idea that has finally found its time.

Evaluation in imperative languages (such as Java and C#) happens one command at a time. The commands typically need to happen in order to ensure that the program executes as expected. When mutability is removed, and functionality applied, order of execution no longer matters. The language and compiler can now execute commands out of order.

Allowing commands to execute out of order has the obvious benefits of allowing multiple threads to handle what previously could only be handled by one thread. But an unexpected twist is that now collections of data can be evaluated lazily. Lazy evaluation means that you can define how to get the data for a collection such as a vector, but the program can hold off on generating the data until later. Once the data is defined, it is immutable, but lazy evaluation means the definition doesn’t have to happen right away.

Lazy evaluation allows you to define infinitely large datasets without fear of running out of memory or clogging up the CPU. The data gets evaluated and defined when it is needed, and not a moment before. Imagine defining the entire Fibonacci Sequence, all decimals of PI, an infinitely long sequence of minion servers round robin style or the entire incoming message from a socket without any worries about running out of memory.

That’s all a side effect of making data immutable and functions functional.

Another side effect of adopting functional programming is that unit testing becomes much easier. You don’t have to mock up unrelated variables and objects because functions always returns the same values when given the same arguments. Functions that are self contained are easier to write unit tests for.

Finding bugs is easier. Anyone that has done a lot of programming has dealt with stepping through code while watching individual variables to see how they change. Variables in functional code are immutable. Once they are defined, they don’t change. Once you glance at the value while stepping through code, there are no questions about how the variable changes later on. The variable never changes. Also, you are sure that any function will return the same value given the same arguments, so you don’t have to figure out what the function is going to return after you’ve seen it once. Nothing changes a function’s result when the arguments provided are the same.

A seamingly unusual feature of functional languages is that functions and data are interchangeable. That means functions can be assigned to variables and passed around just like data. At first, this may seem strange, but it allows the creation of higher-order functions that take functions as arguments. In a moment, you’ll see that higher-order functions play a key role in eliminating the need for while and for-loops.

A surprising side effect for some is that for-loops and while-loops don’t exist in functional programs. For-loops and while-loops of the imperative programming world are replaced by higher order functions and infinite tail recursion. If you have a long imperative programming career behind you, you’ve probably had it drilled into you that recursion is bad. However, if done correctly with a language that supports it, infinite recursion works very similarly to a while-loop and without the worries of mutable variables that while-loops cannot exist without.

For-loops are a staple of most languages used commercially, such as C, Java, and C#. We imperative programming old-timers often can’t imagine a world without for-loops. However, without us even realizing it, for-loops generally fall into doing the same limited number of tasks over and over. Meaning we’ve been writing the same code to do the same tasks, instead of using prewritten libraries to perform the repeated tasks.

Even us old programmers know the admonision, “Reuse, reuse, reuse!” You never write code to do something that a library has already been written to do. However, we keep writing for-loops. How many times have you written a for-loop with the intention of applying some operation to every element of a collection or array? How many times have you written a for-loop to generate one value based off all the values in a collection or array? In each case, you could take a collection and a function (if your function is treated like a variable) and call a higher order function such as apply() or reduce() instead of writing yet another for-loop.

This is just a brief overview of functional programming. There are many other incredible features available in functional languages. One of my favorites is Software Transactional Memory found in Clojure. (Think database transactions for regular programming.) Even before concurrent programming became necessary for regular programs, functional programming offered a full range of incredible features not available in the imperative programming world. We should all be grateful that now we can take advantage of those features in our favorite programming languages.

You can see a complete list of functional languages here.

You can see a list of my functional programming tutorials and essays here.