Chapter 1. What Is Functional Programming?

Functional programming? Functors? Monads? “I’m not a mathematician!” you might say. How can I learn these esoteric concepts? And why would I want to? These concerns are totally understandable. But the truth is you don’t need to be a mathematician to be a functional programmer. The fundamental concepts of functional programming are easy to understand when presented in a clear, straightforward way. And that is what this book is about. Making functional programming understandable and practical. But why would you want to learn functional programming?

Picture this. It’s 10 pm and you are totally stuck while trying to fix a bug in a program you need to hand in in the morning. The problem seems to be centered around a variable called ratio. The problem is that depending on the state of the system you are modeling, the variable ratio keeps changing. Your frustration builds. Or you have a deadline at work and there is an elusive bug in your microservice that you are chasing down. The problem seems to be in two nested for loops in which variables are modified in a fairly complex way. The logic is complex and you don’t quite see the solution. If only there were a way to write programs in a way in which the value of variables would not change! Functional Programming to the rescue.

So, what is functional programming? What makes one language functional and another not functional? The truth is that to some extent it is a matter of degree. You don’t have to follow every principle that falls under the heading of functional programming. Some people will try to follow all of them, and others will pick and choose. It is totally up to you. Functional Programming (FP), is a paradigm, an approach to programming, a way of breaking up the world and putting it back together in code. It involves both how we organize that piece of the world we are modeling and how we organize and structure the code.

In order to better describe the essence of FP, let us begin by contrasting it with Imperative Programming and Object Oriented Programming (OOP). There are others, such as Logic Programming, but the three mentioned, are by far, the most popular. Imperative is what you might think of as plain old programming. It’s what programming was before functional and object oriented programming. In imperative programming, you write functions or procedures, use for loops and while loops and mutate state often. Languages like C or Pascal are typical imperative programming languages. Then there is OOP. Currently the most popular paradigm, OOP is a process of modeling the world as a collection of objects. Each object has state and methods, which are operations representing behaviors specific and relevant to that object. As the program runs, the state of the objects changes. The benefits of this approach include encapsulation, which means the state and methods that belong to an object, exist, at the code level, within the object. This is a much better idea than letting the state be scattered all throughout the code because managing mutable state is just plain difficult. You have multiple variables, possibly many, and their values are changing. The approach of FP is to acknowledge this and attempt to minimize, if not erradicate, changing state altogether. Ultimately, it is not always possible to have no mutable state at all and so the standard FP approach is to isolate that part of the code that mutates state.

Immutability

The single most important aspect of FP is immutability.1 Generally speaking, this means a lack of change. Something is considered immutable, if we cannot modify it in some way. In functional programming this means a few things. Once a variable is set, its value cannot be changed. If x = 3 at the beginning of a program, it has that value for the remainder of the program. Does that mean that if a program is written in a functional style, and a person’s age changes, this change cannot be modeled? Of course not. That would be a disaster for functional programming. There are techniques like copying that allow us to efficiently, manipulate our code, without ever mutating state. Consider the following simple for loop in Java that prints the numbers 0 to 99.

Java

for (int i = 0; i < 100; i++) {
    System.out.println( i );
}

This type of code occurs all the time. You might wonder how we could possibly express this in an immutable way. It seems that the essence of this code is the changing of the value of the variable i. A common approach in FP is to use recursive functions. A recursive function is a function that calls itself. In the case of the above code, you can put the code in a function and then call the function on the next value of i, in each iteration. It might look something like:

Java

void f(int i) {
    if (i > 99) {
        return;
    }
    else {
        System.out.println( i)
        return f(i+1)
    }
}

f(0)

Now this code is a little longer but it does not mutate any state. If you know a little about FP, you might know that the return type of void is a sure giveaway that there will be side effects.2 A side effect is anything that affects the program outside of the function. Things like writing to a file, throwing an exception or modifying a global variable. The above example is just meant to show one way of avoiding the mutation of state. You have probably been mutating state your whole programming career and it likely seems indispensible. But remember two things

Note

1) It feels very natural to mutate state.

2) Mutating state is a major cause of code complexity.

The good news is that with practice, the FP way will feel just as natural.

Let us consider another technique for avoiding the mutation of state. Imagine you have an object, which has a property, or field and it changes. Remember, I never made the claim that this never happens. That would be absurd. The point is to model this situation without mutating a variable in the code. Let us consider a Java example first.

Java

public class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }


    public static void main(String[] args)  {
        Person person = new Person("Carl", 32);
        //A year passes
        Person changedPerson = new Person("Carl", 33);  1
        System.out.println(changedPerson);
    }
}
1

Instead of modifying the value of age in the Person object, we create a new object and initialize the new age value in the constructor. Let us now look at an example in Python.

Python

class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def main():
        person = Person("John",22)
	#One year later
	changedPerson = Person("John",23)

One year passes and we need the person object to reflect this. But we can’t modify the value age. So we create another immutable object with the age variable initialized to 23

Let us now look at an example in Scala.

Scala

case class Person(name: String, age: Int) 1
val person = Person("Katherine", 25)      2
val changedPerson = person.copy(age=26)                       3
1

Declare a case class

2

Create an instance of the class

3

This line makes a new instance of Person and initializes age to 26. No state has been mutated.

You may recall that I believe immutability to be the most important aspect of FP. Lots of mutable state in a program is a source of many bugs. It’s simply not easy to keep track of all the changing values. Above, we have seen some examples of how to get around the apparent need to mutate state. It takes some getting used to but with a little practice, using these techniques may even start to seem natural.

Referential Transparency

You have seen some examples of immutability. Now we will look another crucial piece of what makes up FP, Referential Transparency. We say an expression is referentially transparent if we can replace it with its value anywhere in the code. You might think, upon first hearing about this, that you can always do this. Let us consider a simple example of a non referentially transparent function.

Java

today()

If I call this function, and get, say May 29th, 2021, and I replace its body with this value, and then call it tomorrow, I will get the wrong answer. It might seem impossible to express this function as a referentially transparent function but we will see how to approach this in a functional way later in the book.

Here are a few more examples of non referential transparency:

  • A function that returns a random number. Obviously you can’t replace the body of the function with a value you get when you call it once.

  • A function that throws and exception. Exceptions are generally avoided in FP. I will come back to that later.

It probably seems that if we throw out all non referentially transparent functions (and that is what we will aim for), that we will lose some valuable capabilities. It seems there will be many useful things we need to express but will not be able to. Rest assured, there are functional ways of expressing these things and once you get use to them, they may even seem more uniform and simpler.

A related concept that you will see in writings about functional programming is purity. I am afraid to say there is some confusion in the literature about the relationship between purity and referential transparency and not everybody agrees on the meanings of these terms. Generally, a function is said to be pure if it has no side effects and for a given input, always returns the same output. This basically means that if you put in the same input,you get out the same output. If the input is x and the output is y, no matter how many times you call the function with x as the input parameter, the function will return y. A side effect is anything that happens outside of the context of the function. Writing to a file and throwing an exception are two examples of side effects. Forget for the moment that we need to write to files, (though arguably we don’t need to throw exceptions) and think how nice it would be if everytime we call a function with the same input parameters, we get the same output and nothing, outside of the function is changed. That is something we enjoy in functional programming. Because different people have different views on this, and because the differences between referential transparency and purity are subtle, I will treat the two terms as synonymous.

Now, I said you don’t have to be a mathematician to write functional programs and you don’t. But functional programming does come from mathematics. A field called lambda calculus, to be precise. Much of it also comes from a field called category theory. Category theory has much to do with functions. And in mathematics, functions are what we pure. When a programmer looks at an expression like x = x + 1, she says ah, increment the variable. When a mathematician looks at x = x + 1, she says No, its not.3

Now what would an impure function look like?

Scala

object Main extends App {
 def impureFunction(x: Int): Int = {
 import scala.util.Random
 return Random.nextInt(100) + x
 }
 println(impureFunction(5))
 println(impureFunction(8))
}

The two function calls will very likely return different output values for the same input value. This function is not pure. We have said mathematical functions are pure. Well, programming has gained quite alot from this mathematical approach. Functional programs are clean and pure and elegant. The functional style of programming may take a little bit of getting use to at first,but as we gradually move through the basic ideas of functional programming in this book, you will little by little start thinking like a functional programmer. Your functions will be pure and your code will be clean. The biggest benefit, however, of writing functional programs is that you will have a much stronger expectation that your programs will be correct.

Let me make an important point here. We can’t ultimately just define what FP is in a negative way. We can’t say its the same as ordinary programming except that we leave out this and this and that and the other thing. The hard part, the part accomplished by the many creators of FP is how to express everything we need, in a functional way.

Higher Order Functions

Functional Programming is all about functions. What we want, in a functional programming language, is the ability to treat functions as first class citizens. This means we should be able to pass them as function parameters and return them from functions as return values. Let’s discuss why higher order functions are an important part of functional programming. One key goal in functional programming is to get to the heart of the matter. This means we need to be able to express concepts concisely in our language. If we want to square every integer in a list, for example, we shouldn’t have to loop through the list and modify each number by squaring it. We should be able simply to directly apply a square function to every element of the list simultaneously, as it were. The map function, in many languages, allows us to do this. It allows us to work at a higher level of abstraction. That higher level corresponds to a higher order function. We will see this as a major theme as we proceed.

Example 1 (an imperative approach)

Python

def square(nums):
    squared = []
    for i in nums:
    	squared.append(i*i)
    return squared

Example 2 (a functional approach)

Python

def square(nums):
    return map(lambda n: n*n, nums)

As you saw in chapter 2, lambda is a way of creating an anonymous function; that is, creating a function with out a name. The map funtion that acts on members of a list, and all at once, applies it to all the elements of the list.

Lazy Evaluation

Another component of functional programming is lazy evaluation. This simply means an expression is not evaluated until it is needed. This is not, strictly speaking, necessary for a language to be functional but often languages which are, by nature, more functional, tend to be lazy. Haskell, for example, is lazy by default. Most popular languages are not lazy though, and they use, what is called, eager evaluation. This means an expression is evaluated the first (and every) time it is encountered. As you’ll see in the example below, there are two benefits of lazy evaluation:

  • It allows you to define control flow structures in your code directly as opposed to having to be operators built into the language.

  • It can speed up performance.

Imagine you want to define your own if statement. Let’s call the function myIf. You might want to add a logging line to every if statement, for example. If you try the following, you will encounter a problem.

Scala

def myIf(condition: Boolean, thenAction: Unit, elseAction: Unit): Unit = if (condition)
      thenAction
    else elseAction

Can you see the problem with this definition? With eager evaluation, which most common languages have, when the function is called, the first thing that happens is that all of the parameters are evaluated. So in the case of myIf, both the thenAction and the elseAction will be evaluated when you only want one of them to be evaluated, depending on the condition variable. However, with lazy evaluation, this would work. In this and related cases, it would allow you to write your own control statements. Another benefit is performance improvement in certain situations. Since the lazy code is only evaluated when it is needed, it is often the case that it is actually evaluated less than it would be in the eager evaluation case. This can speed up the program.

In Scala we can use call by name parameters. In the code below, thenAction and elseAction are only evaluated when they are needed. That is, they are evaluated lazily. The following will work as expected.

Scala

def myIf(condition: Boolean, thenAction: => Unit, elseAction: => Unit): Unit = if (condition)
      thenAction
    else elseAction
}

Thinking likek a functional programmer

In this book, we will focus on how to think like a functional programmer. This presupposes, of course, functional programmers think a certain way. While there are a wide range of approaches to functional programming, there are basic ideas functional programmers use and and basic ways of thinking. Functional programmers don’t mutate state, for example. That is once a variable has been set, it is never changed. Also, functional programmers tend to use lots of higer order functions. These are functions that take other functions as parameters and/or return a function as a return value. But to know how a functional programer really thinks involves knowing a set of idioms, or patterns that promote functional code. It’s all well and good for me to tell you not to mutate your variables, but unless you know how, specifically to do this, implementing immutability may not make any sense. In other words, patterns are an important part of functional programming.

Now you may have heard that functional programmers don’t really care as much about patterns as object oriented programmers do. This is largely mistaken. What is true is that the term pattern, in the context of FP refers to something different than the Gang of Four patterns. The Gang of Four patterns were developed in the context of object oriented programming. Patterns like the Prototype, Proxy and Flyweight patterns. These can largely be implemented in a functional style and are useful in the design of programs. But there is nothing particulary functional about these type of patterns. One might say they are functional - neutral. There is another type of pattern that is quintessentially functional and these are ideas that come from category theory. We will address this in some detail in chapter 3.

The Benefits of Functional Programming

The benefits of functional programming are by now clear. It aids us in our quest for bug-free code. Or as close to bug-free code as is possible. And how does it do this? By rewiring our brains, as it were, so that we no longer see the world as a mass of objects each with its own changing state and processes that transform that state in such a whirlwind of complexity that we can hardly wrap our minds around it. There is no doubting it now. State is the culprit. When things change, we need to keep track of them. And that is the problem. A multitude of variables and objects each with its own state, and each with its own set of transformations modifying it. Many a night has been spent by a tired programmer searching for a bug created by a change in state somewhere in the program. There is only so much complexity the human mind can bring under its control. Only so much complexity we can take, before we start writing code that isn’t quite correct. But ’wait’, you say. ’The world is made up of objects. And those objects have state, and that state changes over time! So we are right to model the world this way. That’s exactly how the world is!’ And to that, I respond, maybe, maybe not. There is another way of looking at the world, however, which might make thinking functionally easier. We will address that at a later time.4

For now,the important thing is to realize that writing bugfree software is not something we really know how to do. I knew a Computer Science professor who once started off his introduction to programming class with the sentence The human race does not yet know how to program. A bit dramatic, perhaps, but true. Projects typically come in above budget and take much longer than predicted. The reason is complexity. Programming is the art and science and engineering of managing complexity. Functional programming brings with it tools we can use in an attempt to restrain and control this complexity. Tools like immutability, referential transparency and higher order functions, to name a few. Master these tools and your code will be better, and more bug-free.

FP can improve productivity

So Functional Programming is a programming paradigm. What other paradigms are there?

The most popular is arguably5 something called Object Oriented Programming, or OOP. If you have programmed in Java or C# or C++ or Python for example, you are probably familiar with this way of programming. In this case, the world is modeled as a collection of objects each with its own state and its own behavior. There are many benefits of OOP. But even with these benefits, our code still suffers from coming in overbudget and overtime. Before FP and OOP became popular, there was procedural programming, also called imperative programming. Some might distinguish between the two, saying that procedural is built upon procedures. OFten the two terms are treated as synonyms. On the surface, procedural resembles functional programming a bit. Functions are the main things and there are no objects or classes. But upon closer look, one sees that state is mutable, functions are not referentially transparent and procedural languages didn’t necessarily have higher order functions. Much complexity C and Pascal are two examples of procedural programming languages.

You could argue that the best programmers will produce better code no matter what paradigm they use and this is probably true. But the question is if we have two developers of equal skill, one working with an object oriented approach and the other working with a functional approach, who will be more productive. I believe that the clarity, power, and higher level of abstraction will allow the functional programmer to produce more correct code, faster.6

FP is fun!

But there is another reason to program with functional programming. And this is perhaps the most important reason yet. Writing code with functional programming is fun! And it is fun for deep reasons. Functional programming lets you get to the heart of the matter. It lets you cut to the chase and spend more time coding on the subject matter. It is at a sufficiently high level of abstraction that it feels as if you are manipulating important, relevant concepts instead of moving drudgingly through low level details that closely model what a machine does.

The history of programming languages, from one perspective, is largely a story of ever increasing abstraction level. The higher the level, the easier it is to avoid manipulating masses of detail. But abstraction does not have to be hard. And functional programming is showing us this. Yes, there is a learning curve. But it is possible to shorten this curve by making the change little by little. If you currently code in Java or Javascript or Python for example, it is possible to gradually include idioms and structures and practices that will make your code more functional and before you know it, you will start to naturally rely on functional idioms for the power and tendency toward simplification they gives you. If you read this book carefully and study the examples given and start to incorporate some functional ideas into your code, you will soon see great benefits. You may even decide you want to investigate some programming languages that provide more support for the functional paradigm.

A note about the examples in this book. I will be providing examples in various languages. This is to demonstrate the way different langauges implement functional ideas. The languages will include Java, Python and Javascript. I will also provide many examples in the Scala language, especially when describing category theory. I would argue that a language like Scala allows for more concise functional constructs and in many cases it may drive a point home to show how to do it in Scala. There are other languages I could have used for this purpose; other languages which are functional to one degree or another; Haskell or Clojure or ML for example. I simply felt that the clarity of Scala and the fact that it often resembles pseudocode, made it a good choice for many of the examples. In fact, if you have been using Java and you are interested in functional programming and you have the opportunity, you may want to try out Scala. I would argue that especially for greenfield projects, you might find it useful.

Before we dive, in chapter 3, into functional patterns, let us spend a chapter on the various languages we will be using for examples and in particular, the constructions, like Option and List, you will need to understand what follows.

1 Some might argue that referential transparency is the most important.

2 In FP, all functions should return a value. void is a sure sign of side effects.

3 This is meant to be a joke but I’ve experienced these reactions first hand.

4 A link to the final chapter will go here when that chapter is available

5 While there is more OOP code in existence currently, there is clearly a move in the direction of FP among many developers. It remains to see how this will play out. Perhaps a hybrid approach that mixes both approaches will become the norm. Or perhaps FP will just continue to get more popular.

6 oclas i.e. Opinion, clearly labeled as such.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.214.32