What is Functional Programming?

Intro: What is Functional Programming?

Welcome to the first lesson in this course on functional programming. The goal of this course is to develop an understanding of what functional programming is, what the advantages of functional programming are, and to gain a working knowledge of how we can actually write functional programs.

There are many advantages to functional programming, including programs that are easier to reason about, programs that are easier to test, programs that are easier to refactor, and programs that can be reused to a much greater extent than then can other paradigms. But in order to fully appreciate and understand how these advantages come about, there are some fundamental things we have to know.

So in this lesson we’re going to start by tryng to understand what the term “functional programming” actually means.

Although people use the term “functional programming” in different ways, in this course “functional programming” will mean a very specific, precisely defined concept.

The definition of functional programming that we will be using is simply programming with pure functions. Programming with pure functions.

So let’s unpack that a bit. Before we can understand what a PURE function is, we need to know what a function is.

What is a function

A function is a very simple but very powerful idea. It is simply a relation or a mapping between a set of inputs and a set of outputs.

We could use a function to relate any set to any other set. It may be a mapping between every person on earth and the country they live in, or mapping every color in a crayon box to an integer. Maybe we’re mapping every car on a used car lot to a brand or make (car1 is a Honda, car2 is a Chevrolet, etc.) These relationships can all be expressed as functions.

The definition of a function is very precise. The formal definition of that a function relates EACH element of a set with EXACTLY ONE element of another set.

relates EACH element of a set with EXACTLY ONE element of another set.

Let’s analyze both of those things:

Firstly, it must relate EACH element of a set. This means that if we have a function that maps elements of some set X to some element in Y, that EVERY element in X is related to some element in Y. Every input has to map tosomething. There are no cars on our used car lot that don’t map to a make.

The second thing we mentioned is that it relates each element to EXACTLY ONE element of another set. In other words, we cannot have a function that says that for some value in our input set x, the output is 1 or 2. We can’t say this car is both a Toyota and a Honda. Each input has exactly one output.

One thing to note about that last point is that nothing about the formal definition of a function precludes the possibility of mapping multiple members of our input set to the same element or member of our output set. We can have several cars that are Toyotas (several seperate inputs with the same output), but we can’t have one car that is both a Toyota and a Honda.

Let’s look at some examples to help make this clear.

Let’s define two sets. One will be our input set and one will be our output set. By the way, there are more formal terms for “input” and “output” which is domain and codomain. The domain is the input set and the codomain is the output set. So we can say a function is mapping from some domain to some codomain.

Let’s look at a simple example. Let’s define two sets

<code>X = <span class="hljs-list">{Red, Green, Blue}</span>
Y = <span class="hljs-list">{10, 5, 7}</span>
f(<span class="hljs-keyword">Red</span>) = <span class="hljs-number">10</span> 
f(<span class="hljs-keyword">Green</span>) = <span class="hljs-number">5</span>
f(<span class="hljs-keyword">Blue</span>) = <span class="hljs-number">7</span></code>

Is this a function? Well, let’s see. Again, in order to be a function it must relate each element in our input set, or the domain, to exactly one member of the output set, or the “codomain”. f is therefore a function because every element in our set X of colors maps to something in the set Y and it maps each of those elements to exactly one member of Y.

We can define f in a way that wouldn’t be a function if we, for example, had f of Red equal 10 OR 3.

In other words

<code><span class="hljs-function"><span class="hljs-title">f</span><span class="hljs-params">(<span class="hljs-variable">Red</span>)</span> = 10
<span class="hljs-title">f</span><span class="hljs-params">(<span class="hljs-variable">Red</span>)</span> = 3
<span class="hljs-title">f</span><span class="hljs-params">(<span class="hljs-variable">Green</span>)</span> = 5
<span class="hljs-title">f</span><span class="hljs-params">(<span class="hljs-variable">Blue</span>)</span> = 7</span></code>

This isn’t a function because Red is mapped to more than one element in the set Y.

Let’s change it up a little bit. What if we defined it like this?

<code><span class="hljs-function"><span class="hljs-title">f</span><span class="hljs-params">(<span class="hljs-variable">Red</span>)</span> = 10
<span class="hljs-title">f</span><span class="hljs-params">(<span class="hljs-variable">Blue</span>)</span> = 7
<span class="hljs-title">f</span><span class="hljs-params">(<span class="hljs-variable">Green</span>)</span> = 10</span></code>

Is this is a function? Well, is each element in our domain mapped to an element in our codomain? Yes. The fact that Red and Green both map to 10 is not a problem because nothing in our formal definition of a function says that we can’t allow many elements in our input set relate to the same element in our output set. What violates the definition of a function is when the same input element maps to different output elements.

Let’s look at another simple example. We define the function f of x is x divided by 2.

f(x) = x / 2

You can read that as “f of x is x divided by 2”.

Is this a function? Yes because each input has a single output which is whatever x is divided by two.

Ok so a function has a specific meaning and we know what that is now. We mentioned that functional programming is programming with PURE functions.

What exactly is a PURE function?

Pure Functions

When we say we that a function is pure we mean that all the function does is compute some result given an input and nothing else. Our function from colors to numbers that we discussed earlier is a pure function. It does nothing other than map a color to a number.

So what would an impure function be? An impure function is a function that has some observable effect on the execution of a computer program. In other words, an impure function is a function with side effects.

We’ll go into some examples soon to explain what that means more concretely, but one thing to note is that in math, as opposed to programming, calling something a pure function is redundant. There’s simply no notion of functions with side-effects in math.

We’re going to look at an example of a pure function in JavaScript.

In Javascript we can define a function as follows:

<code><span class="hljs-keyword">var</span> f = <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(x)</span> {</span>
    <span class="hljs-keyword">return</span> x;
}</code>

var f declares a variable named f. The equals (=) sign is the assignment operator which assigns a value to a variable. In this case it assigns a function definition to the variable f. function(x) means we are defining a function which takes a single argument or input. The curly braces wraps the actual body of the function. The return keyword specifies the value that the function returns. This function is simply the identify function:

<code><span class="hljs-attribute">f(x) </span>= x</code>

Ok so with that, let’s look at a pure function in JavaScript:

trim is a function in JavaScript which maps Strings to Strings. It takes a string and returns a string with the whitespace removed from both sides of the string.

<code><span class="hljs-keyword">var</span> trimString = <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(string)</span> {</span>
    <span class="hljs-keyword">return</span> string.trim();
}
<span class="hljs-keyword">var</span> myString = <span class="hljs-string">" hello    "</span>;
<span class="hljs-keyword">var</span> trimmed = trimString(myString);
console.log(trimmed); <span class="hljs-comment">// prints "hello" (spaces removed)</span></code>

The first line declares a function called trimString. The function just uses thetrim function. So trimString takes a String as input and outputs a String. That last line, console.log, is how you print something to the screen.

Our trimString function is pure because it has no side effects. It simply maps every input String to an output String. We’re going to explore a much more formal idea of what a pure function is in a moment, but we want to give you the basic idea first.

Let’s take a look at an impure function, again in Javascript.

<code><span class="hljs-keyword">var</span> gimmeOne = <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(array)</span> {</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">array</span>.pop();
}
<span class="hljs-keyword">var</span> myArray = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
<span class="hljs-keyword">var</span> lastElement = gimmeOne(myArray)
console.log(lastElement); <span class="hljs-comment">//prints 3</span></code>

Here we wrote a function called gimmeOne which itself just uses the popfunction which removes the last element from an array and returns that element. pop is an impure function because it does more than just return the last element of the array. It has the side effect of mutating the array we are popping. To see that we just need to inspect the array before and after callingpop.

<code>var myArray [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
console.<span class="hljs-keyword">log</span>(myArray); <span class="hljs-comment">// prints [1, 2, 3]</span>
var lastElement = myArray.pop();
console.<span class="hljs-keyword">log</span>(lastElement); <span class="hljs-comment">// prints 3</span>
console.<span class="hljs-keyword">log</span>(myArray); <span class="hljs-comment">// prints [1, 2]</span></code>

So pop definitely did something more than just return the value 3 and we can observe the effect by simply taking a look at our array before and after using the pop function.

Another way to describe a pure function is that everything the function does is captured in the value it returns. For pop this clearly isn’t the case because when we called pop on our array containing the elements [1, 2, 3], the output of the function was just the value 3, but pop clearly did more than just compute that result because when we later inspected the same array right after calling the pop function we found that an element had been removed!

But even this explanation isn’t formal enough. The formal way of defining pure functions is through a concept called “Referential Transparency”.

The truth is that referential transparency is a characteristic of expressions, not just functions. We’re using the term expression to mean any part of a program we write which can be evaluated to some result or value. 1 + 1 is an expression, for example. It evaluates to 2. someString.trim() in Javascript is an expression which evaluates to a some String with whitespace removed from the both sides of it.

Ok, so we’re talking about expressions now. We’ll get back to functions a bit later. So what does it mean for an expression to be referentially transparent?

I’m going to state the definition of referential transparency and it is a bit of a mouthful. Don’t worry about memorizing it or anything..the idea will become very clear with an example.

An expression e is referentially transparent if, for all programs p, all occurrences of e in p can be replaced by the result of evaluating e without affecting the meaning of p.

Again:

An expression e is referentially transparent if, for all programs p, all occurrences of e in p can be replaced by the result of evaluating e without affecting the meaning of p.

Let’s take an example similar to someting we saw earlier.

<code><span class="hljs-built_in">var</span> originalString <span class="hljs-subst">=</span> <span class="hljs-string">"  hello  "</span>;
<span class="hljs-comment">//      </span>
<span class="hljs-built_in">var</span> trimmed1 <span class="hljs-subst">=</span> originalString<span class="hljs-built_in">.</span>trim();
<span class="hljs-built_in">var</span> trimmed2 <span class="hljs-subst">=</span> originalString<span class="hljs-built_in">.</span>trim();
console<span class="hljs-built_in">.</span><span class="hljs-keyword">log</span>(trimmed1); <span class="hljs-comment">// prints "hello"</span>
console<span class="hljs-built_in">.</span><span class="hljs-keyword">log</span>(trimmed2); <span class="hljs-comment">// prints "hello"</span></code>

In this little program, originalString is an expression. How can we evaluate if it is referentially transparent. Well, according to the definition, if we can replace every occurence of the expression originalString with what it evaluates to, which in the case is the literal string ” hello “…if we do that and the program still has the same meaning, then that expression is referentially transparent.

Here’s what that looks like:

<code><span class="hljs-reserved">var</span> trimmed1 = <span class="hljs-string">"  hello  "</span>.trim();
<span class="hljs-reserved">var</span> trimmed2 = <span class="hljs-string">"  hello  "</span>.trim();
<span class="hljs-built_in">console</span>.log(trimmed1); <span class="hljs-regexp">//</span> prints <span class="hljs-string">"hello"</span>
<span class="hljs-built_in">console</span>.log(trimmed2); <span class="hljs-regexp">//</span> prints <span class="hljs-string">"hello"</span></code>

All we did was replace the reference originalString with the value it refers to. That substitution had no effect on our program. This program and the one before are the exact same program. Again, we wanted to test whether the expression originalString is referentially transparent. To do that, we applied a simple test. We just did a find and replace and replaced every occurence oforiginalString with " hello ". Since the two programs, before and after, are identical, we can say that originalString is referentially transparent. The expressions trimmed1 and trimmed2 are also referentially transparent which means that we can also replace each occurence of those with the literal"hello" in whatever program we write and it wouldn’t change our program at all.

Ok, so that’s referential transparency. Let’s look at an example that violates referential transparency.

Once again we’ll take an example we’ve already worked with:

<code><span class="hljs-keyword">var</span> someArray = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
<span class="hljs-keyword">var</span> anArrayElement = someArray.pop();
<span class="hljs-keyword">var</span> someString1 = anArrayElement.toString();
<span class="hljs-keyword">var</span> someString2 = anArrayElement.toString();
console.log(someString1); <span class="hljs-comment">// prints 3</span>
console.log(someString2); <span class="hljs-comment">// prints 3</span></code>

Let’s apply the referential transparency test the same way we did earlier. If the expression anArrayElement is referentially transparent then we’d be free to replace every occurence of it with whatever it evaluates to. For this example, that would mean finding every place where we use the anArrayElementreference and replacing it with the expression referenced by anArrayElementwhich is someArray.pop(). Let’s try:

<code>var someArray = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]<span class="hljs-comment">;</span>
var someString1 = someArray<span class="hljs-preprocessor">.pop</span>()<span class="hljs-preprocessor">.toString</span>()<span class="hljs-comment">;</span>
var someString2 = someArray<span class="hljs-preprocessor">.pop</span>()<span class="hljs-preprocessor">.toString</span>()<span class="hljs-comment">;</span>
console<span class="hljs-preprocessor">.log</span>(someString1)<span class="hljs-comment">; // prints 3</span>
console<span class="hljs-preprocessor">.log</span>(someString2)<span class="hljs-comment">; // prints 2</span></code>

What happened? In the first program we had

<code>console.<span class="hljs-keyword">log</span>(someString1); <span class="hljs-comment">// prints 3</span>
console.<span class="hljs-keyword">log</span>(someString2); <span class="hljs-comment">// prints 3</span></code>

but in the second one we have:

<code>console.<span class="hljs-keyword">log</span>(someString1); <span class="hljs-comment">// prints 3</span>
console.<span class="hljs-keyword">log</span>(someString2); <span class="hljs-comment">// prints 2</span></code>

What happened is that the expression anArrayElement is not referentially transparent because the method pop actually does more than just return the last element of a list! It removes the element as well. That’s the side effect. That’s why it’s not a pure function and the analysis we just did shows how this breaks referential transparency.

Now that we have a precise definition of referential transparency, we can define the term “pure function” more formally. We call a function pure if when we call the function, the resulting expression is referentially transparent whenever the argument we call it with is referentially transparent.

In other words, say we have a function named fizzle which takes a single argument, call it x. In JavaScript that might look like var fizzle = function(x) { // doesn't matter what the function does }. If the expressionfizzle(x), i.e. if the expression of calling the function is referentially transparent whenever we use a referentially transparent x argument then we say that the function fizzle is “pure”.

You’ll have a chance to review this idea some more, but these examples are a good starting point. The concept of referential transparency gets to the essence of what functional programming is. Functional programming means that every expression in our program is referentially transparent. The power of functional programming and the many advantages that it has all flow directly from this property. Referential transparency lets us use a style of reasoning known as the “substitution model” when we think about our programs. In other words, computations in our program work very much the way we solve equations in algebra. In algebra we simplify each part of an expression, replacing each term with an equal term until we arrive at the final value. Referential transparency lets us do the same thing in our programs.

Here’s what that looks like from our earlier example:

<code><span class="hljs-keyword">var</span> originalString = <span class="hljs-string">"  hello  "</span>;
<span class="hljs-keyword">var</span> trimmed1 = originalString.trim();
<span class="hljs-keyword">var</span> trimmed2 = originalString.trim();
<span class="hljs-keyword">var</span> joinedStrings = trimmed1 + trimmed2;</code>

We can reason about the evaluation of this program in a very simple way. First, we replace each occurence of originalString with the literal String " hello ":

<code><span class="hljs-keyword">var</span> trimmed1 = <span class="hljs-string">"  hello  "</span>.trim();
<span class="hljs-keyword">var</span> trimmed2 = <span class="hljs-string">"  hello  "</span>.trim();
<span class="hljs-keyword">var</span> joinedStrings = trimmed1 + trimmed2;</code>

Then we’ll do the same for trimmed1:

<code><span class="hljs-keyword">var</span> trimmed2 = <span class="hljs-string">"  hello  "</span>.trim();
<span class="hljs-keyword">var</span> joinedStrings = <span class="hljs-string">"  hello  "</span>.trim() + trimmed2;</code>

And again for trimmed2:

<code>var joinedStrings = <span class="hljs-string">"  hello  "</span><span class="hljs-preprocessor">.trim</span>() + <span class="hljs-string">"  hello  "</span><span class="hljs-preprocessor">.trim</span>()<span class="hljs-comment">;</span></code>

then we simplify each of the " hello ".trim() expressions:

<code><span class="hljs-keyword">var</span> joinedStrings = <span class="hljs-string">"hello"</span> + <span class="hljs-string">"hello"</span>;</code>

and the final simplification is to concatenate the two Strings:

<code><span class="hljs-keyword">var</span> joinedStrings = <span class="hljs-string">"hellohello"</span>;</code>

Not a very interesting program, but this is the full interpretation of it.

As you can see, the way we reasoned about the evaluation of our program is very straightforward. We can interpret the entire program by hand simply by applying subsitutions the same way we do in math! We replaced all the variables with whatever they referenced and then simplified. When can’t simplify any further, we’re done. This method of interpreting a program by substituting equals for equals is known as as “equational reasoning”.

Another way to look at it is that referential transparency lets us rely on “local reasoning” only which means when we’re analyzing or reasoning about a block of code, we can understand the meaning of it in our program without any other global or contextual information. The example we went through where we broke referential transparency is the perfect example. In that example, we can’t really understand the meaning of someArray.pop() insofar as our program is concerned without first examining the surrounding context. We need to mentally go through the sequence of steps, keep track of the state of the array after pop is called repeatedly! On the other hand, the program where we use the trim function is easier to reason about. We know that every time we see originalString we can just replace it with the literal String " hello " and when we see " hello ".trim() we can replace that with "hello". There’s no context to understand, no global reasoning we need to employ in order to think about our program. We just take an expression, mentally replace it with whatever it evaluates to and that’s all we need!

Complete and Continue  
Discussion

0 comments