Elements of Scala functional Programming

In this blog, we are going to cover 10 fundamental elements of functional programming.

  1. Pure functions and Side effects.
  2. Referential transparency.
  3. First-class function and higher-order function.
  4. Anonymous function.
  5. Immutability.
  6. Recursions and tail recursion.
  7. Statements.
  8. Strict and Non-Strict (Lazy) evaluation.
  9. Pattern matching.
  10. Closure.

Let's start understanding these concepts in detail.

1. Pure functions and Side effects

A pure function is a function that depends only on its declared inputs and its internal algorithm to produce its output. It does not read any other values from “the outside world” — the world outside of the function’s scope — and it does not modify any values in the outside world.

A function that satisfies the below 3 Characteristics are known pure functions:

  1. The input solely determines the output. (There is no other thing like a global variable or content of a file or input from the console that determines the output. It’s only the input parameter value, nothing else. No matter how many times you execute the function as long as the input is the same, the output will be the same.)
  2. The function doesn’t change its input. (Concepts like call by value and call by reference are out of the scope of pure function’s standpoint.)
  3. The function doesn’t do anything else except computing the output. (It means that it doesn’t read from any file or console. It doesn’t print on a console or write some data to the file. It Doesn’t modify any global value or for that matter, anything outside the function also doesn’t perform any I/O.)

You can call a doubleX function an infinite number of times with the input value 5, and you’ll always get the result 10, a function failing to satisfy the above characteristics is known as a function with side effects or impure function.

Conversely, we can state functions like getDayOfWeek, getHour and getMinute are impure functions because they violate the definition of pure functions as their result lies on hidden input or I/O in this example.

With the above definition is also comes to mind that why should we follow writing pure function? Below points states this question.

  1. Encourage safe ways of programming. As no surprise output for the same input will be there.
  2. Composable or modular. (dothis().thenthis().thenthat().thenEnd())
  3. Easy to test. As the output is not printing on the console, only return type is used for output. So return value can be easily asserted.
  4. Memoizable. If it is pure, then the result will be the same then the function with the same input-output can be cached.
  5. Can be lazy. Can be called later when used. Lazy evaluation is discussed later below.

2. Referential transparency

Referential transparency is a measure to check the purity of a function. A function is Referential transparent if evaluating it gives same value for the same argument passed.

3. First-class function and higher-order function

First-class function: If you can treat a function as a value, then it is a first-class function. Below are the characteristics of a first-class function.

  • You can assign it to a variable.
  • You can pass it as an argument to other functions.
  • You can return it as a value from other functions.

In Scala, all functions are first-class functions by default. Below is an example of function f1 which returns a println statement.

def f1 = println("Example of first class function")//To check it just assign function to a variable.
val x=f1

Higher order function: The function that does at least one of the following is a higher-order function.

  • Takes one or more functions as an argument.
  • Return a function as its results.

First-class function example: Here function f2 takes a function as an argument.

def f2(func:Unit) = func//Calling Statement, passing funtion f1 to function f2.
f2(f1)

4. Anonymous Function:

A function that does not have a name is called an anonymous function.

A normal function has below syntax.

def doubler(number:Int):Int = {return 2*number}//Calling Statement
println(doubler(2))

Let's now see anonymous function syntax. This syntax is also known as function literal syntax. The signature and body of a function code are called function literal. Create it where you want to create an inline function for one-time usage only.

val doublerAnonymous= (number:Int) => {2* number} : Int//To call it, function literal is assigned to a variable
println(doublerAnonymous(5))

5. Immutability

Immutability is which cannot be changed. There are 2 keywords in Scala var and val.

Var = Variable. Can be changed.

Val = Value. Cannot be changed.

You cannot reassign values again to Val. Scala as functional programming recommends using val.

Then how can we program without a variable?

  • You can try with recursion if we can rather than loops or you can have a new state of those values rather than using var.

What are the benefits of immutability?

  • Immutability helps us to adopt the mathematical approach and create pure functions. In the real world, objects change their state but in mathematics, the object does not change their state.

In sum(1,3) it doesn’t modify inputs, it creates a new object for output.

Immutability helps us avoid various problems:

  • Immutable objects are more thread-safe. Allows the thread to act on an immutable object without worrying about other threads because it knows no one is modifying the objects.

6. Recursion and Tail Recursion

Recursion is a process where a function calls itself.

In a list of [5,4,3,2,1,0]. Here 5 is head and the remaining list is tail.

How to approach recursion?

  1. Identify the list.
  2. Implement the terminal condition.
  3. Compute the head and recurs with the tail.
//Example of normal recursion
def rFactorial(num:Int) : Int = {
if (num<=0)
return 1
else
return num * rFactorial(num-1)
}
//Calling
println(rFactorial(5))

Recursion uses the stack and till termination is achieved all data remains in stack. So in general loops are better than recursion but by tail recursion, we can end these many calls. And Scala compiler internally converts tail recursion to loops so we can write without vars. You can check differences even by throwing an exception in a terminal condition and check log stack trace printed.

Tail Recursion: A tail call or tail recursion is a function call performed as the last action.

def tFactorial(num:Int, factorial:Int) : Int = {
if (num<=0)
return factorial
else
return tFactorial(num-1, num*factorial)
}
//Calling
println(tFactorial(5,1))

7. Statements

In general definition, a program is nothing but a sequence of statements that modify some program states.

But in Scala this is not correct, In Scala or FP we don’t modify states.

In FP, we have definitions and statements but every statement should have the capability to return a value. Even a Scala loop can return a value.

Even due to this there is no Void, there is a Unit which returns (), so even println() in Scala return Unit.

Since the Scala statement returns a value some people don’t call the statement. They call them an expression. So there might be a case you heard scala doesn’t have statements but only expressions.

But why Scala statements returns?

When you practice FP approach, you will realize that using functional statements allows us to reduce the number of variables in our code. Removing the variables from your code helps you to achieve immutability, which means if you don’t have a variable, you don’t have to mutate it.

//imparitive Approach
def imparitiveResult(marks:Int)={
var result= ""
if (marks>=50)
result="passed"
else
result="failed"
println(result)
}

//functional Approach
def functionalResult(marks:Int) ={
if (marks>=50) "passed"
else "failed"
}

8. Strict vs Lazy Evaluation

Strict: Evaluate the expression now.

Lazy: Evaluate it on the first use.

We can see Strict Evaluation behavior in 3 different scenarios.

  • Variable Assignment.
  • Function parameter.
  • Higher-order function.

Scala evaluates function as soon as we assign it to a val.

Strict Evaluation:

1. Default evaluation in Scala is strict.

2. Scala evaluates function parameters before passing the value to a function. Hence they are evaluated only once.

3. If the parameter value is a function (In the case of HO functions), scala evaluates it inside the body of the function on every use.

4. If you don’t want multiple evaluations of a function value, you can cache it.

Lazy Evaluation:

Lazy val l = Factorial(15)/Factorial(11)

Now it will only be called once we use l for the first time like printing. Then if we print l again factorial will not be called as val is constant and already got value assigned on first execution.

You can also make a function lazy, just assign a function to lazy val.

Why Lazy ?

Laziness is mostly used to create data structures to handle large volumes of data efficiently.

In the case of large data sets if you use a list that gets evaluated strictly and loads complete data, other than that you can use stream as stream does lazy evaluation.

Lazy evaluations can combine operations.

Stream -> Gets one line + applies filter.

List -> Get all the lines + Applies filter to all the lines.

Fibonacci series can’t be generated till infinite if we use the list as the list is strictly evaluated. Rather we can use stream, as the stream is a lazy list of scala and get evaluated when required. Example below.

def fibFrom(a:Int, b:Int): Stream[Int]= {
a #:: fibFrom(b,a+b)
}
//Calling
val feboStream = fibFrom(1,2)
feboStream.takeWhile(_<=10) foreach println

9. Pattern Matching

When we talk about pattern matching, the first thing that comes to our mind is a string matching or regular expression.

But with functional programming, we can do the object-to-object matching.

def myTest(x: Any) = {
x match {
case i: Integer => "It is an integer " + i
case s: String => "It is String " + s
case d: Double => "It's a double " + d
case _ => "Oops! Something Else"
}
}

//Calling
def main(args: Array[String]): Unit = {
println(myTest(1))
println(myTest("Anshul"))
println(myTest(1.11))
}

10. Closure

It is just like a Scala function pure or impure, named or anonymous, but just has 1 difference that it has one or more free variables.

object Closure {

val p=10

def main(args: Array[String]): Unit = {
println(doIncrement(100))
}

def doIncrement(salary:Double) = salary + salary * p/100
}

“p” is not passed as a parameter and not defined as a local variable. “p” doesn’t have any definition or meaning within the function. “p” here is a free variable.

We call it a free variable as “p” is not yet bound to the function with a valid value.

Scala compiler will try to find out “p” in the nearest local lexical environment in which that function was defined and tries to find a binding. So, you can set the value of “p” anywhere outside the function.

Hope you got a quick understanding on elements of the functional programming paradigm. Stay tuned for more blogs. :-)

I would describe myself as a keen learner in data science. I am enthusiastic & motivated to apply my skills in a real-life project which can impact people.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store