A practical introduction to functional programming

Functional Programming (FP) is a simple premise with far reaching implication. We construct our programs using only pure functions — means functions that have no side effects.

Difference between Object Oriented and functional programming

What are side effects ?

In simple terms a function has side effects if it does anything other than simply returning a value.

function with and without side effects

Examples of side effects

1 - Modifying a variable

2 - Modifying a data structure in place

3 -Setting a field on an object

4 -Throwing an exception or halting with an error

5 - Printing to console or reading from console

6 - Reading from a file or writing into a file

All these examples will raise a lot of questions even like how to write a useful program without any of these? Why reading from a file produces side effects, if we pass a file as an argument into the function ? How to write useful programs without loops ? . Well let us look into some of these questions in detail.

Reading from a file or writing into it

For a pure functional program the same set of input parameters to a function always produce the same set of output. This condition is not true in case of read and write operation, the output may change without changing arguments. Later we can discuss this with the concept of “ Referential Transparency ” (RT), This means you can substitute a function call and a given set of parameters with the result the function would return. Reading from a file is therefore not referentially transparent!

Following the discipline of FP is tremendously beneficial because of the increase in modularity that we gain from programming with pure functions. Because of their modularity, pure functions are easy to reuse,test, parallelize. Pure functions are less prone to bugs. Let us discuss the advantages of a pure functional program with an example that has side effects and how to take benefits by removing those side effects.

class cafe{
def buycoffee(cc:creditcard): coffee ={
val cup =new coffee()
cc.charge(cup.price)
}
}

We are taking the syntax of Scala for the purpose of demonstration. In the above example cc.charge(cup.price) is an example of side effect. Charging a credit card requires some interaction to the outside world communication with the bank,or some web services,authorizing the transaction, charging the card if transaction is successful. But our function merely returns a cup of coffee and the other actions happening on the side are side effect. Now let us discuss what are the problems with this side effect, one main problem is how to test the above function ? obviously we don’t want our test to contact the credit card company or bank and charge the card !, this lack of testability suggests a design change.

class cafe{
def buycoffee(cc:creditcard): (coffee,charge)={
val cup =new coffee()
(cup.charge(c))
}
}

The above code solves this problem, Our aim is to credit-card be ignorant on how we charge price or how we contact bank, this approach will improve modularity and will increase testability. Separate from the problem of testability there is another problem, it is the difficulty of re-usability of the buycoffee function. Suppose a customer Jack would like to order 12 cups of coffee. ideally we could just reuse the function by calling it 12 times in a loop. But the non functional implementation has a problem that will involve contacting the payment system 12 times, authorizing 12 separate charges to Jack’s credit card! this will cost Jack 12 times adding the processing fee separately, what can we do about this ? this is were the functional way comes into play. Where cup.charge(c) will accumulate the charge 12 times and returns the charge as a value in addition to returning the coffee.The concerns of actual charging of the credit card can be handled elsewhere in the program. This approach improves testability, re-usability of the code and our programs will be less prone to bugs.

What exactly is a pure function

We already saw how we can remove side effects from our code with the example of coffee shop example, and improved testability,re-usability after removing those side effects. Now let us formalize this notion, what precisely this means to remove side effects. How to write programs that are more easy to reason about ?

A function f with input type A and output type B (written in Scala as A => B pronounced as “A” to “B” or “A” arrow “B”) is a computation that relates exactly every value a of type A to exactly one value b of type B such that b is solely determined by the value of a. Any state change internal or external is irrelevant to the computation of f(a). For example a function intToString having type Int => String will take every integer to a corresponding string. Consider the addition (+) as an example which takes two integers as an input and returns an integer as an output.

Referential Transparency

Let us take the example of addition function as an example, for every two inputs it always returns the same output. For example if we pass 3, 2 to the function the output will always be the same that is 5. So we can substitute the inputs 2 and 3 with 5 it won’t change anything in the program. This is the concept of referential transparency, the expression can be replaced with it’s result without changing the meaning of the program. A function f is pure if the function f(x) is referentially transparent to all referentially transparent x.

val x ="Hello world"
//r1 and r2 are same
val r1=x.reverse
val r2=x.reverse
//Substitute x with "Hello world"
val r1="Hello World".reverse
val r2="Hello world".reverse
X is referentially transparent

In the above example it shows that x is referentially transparent even if we substitute value of x with “hello world” the result will be always the same.The substitution model is similar to the algebraic expression, we move on substituting with values and the result won’t change after the substitution. Replacing all the variables with their referents and substituting into its simple forms. In other words RT enables equational reasoning about programs.

Mutability and Immutability

Referential Transparency

We already saw the advantages of functional programming,substitutional model,RT. Now let us discuss the concept of mutability and immutability with simple examples.

val x= new StringBuilder("Hello")
val y =x.append(" world")
val r1=y.toString()
val r2=y.toString()
#substitute value for y
val r1=x.append(" world").toString()
val r2=x.append(" world").toString()

StringBuilder is a class in java.lang package Here I am using append method in the StringBuilder class to append a “world” with hello. Later I am substituting the value of ‘y’. The output will be.

Hello world world

What happened here is that x was already mutated by the append function, the append function in java.lang.StringBuilder is mutable. All the call to the append will mutate the object x. This invokes a major problem to the parallelism of our programs every time x.append is called from different parallely executing cores it will mutate the object x.

val x ="Hello world"
//r1 and r2 are same
val r1=x.reverse
val r2=x.reverse
//Substitute x with "Hello world"
val r1="Hello World".reverse
val r2="Hello world".reverse
X is referentially transparent

In the referentially transparent example x.reverse can be called any time, within any number of parallely executing cores the result will always be the same. Thus functional approach is a concept that has far reaching implication,which will help to distribute our programs horizontally, improves testabilty and re-usability.