Object Oriented vs Functional Language

Teepika R M
6 min readAug 10, 2020

Though I have been using Scala (Spark) for quite a time now, only after reading a few books related to it and doing some consequential explorations, I have learnt certain new aspects. As learning is like the limitless horizon, I think I could share some in a professional platform like linkedIn, where it can reach the like minded learners . I am planning to split the posts in parts. Here goes the first one.

Having strong base is inevitable. So lets start with understanding what makes Scala special. Scala is powerful because of its dual nature of being object oriented as well as functional. Scala supports object oriented paradigms like encapsulation,polymorphism,inheritance etc,.We all know how Scala works, when we say its object oriented. So lets jump to Scala being functional part. There are two main ideas that drive functional programming. 1) Functions are first-class values 2) Methods should not have any side effects.

1)Functions are first-class values,

What are first-class values? First class values are entities that supports all operations available to other entities. Integer,String etc are first-class values. Similarly in functional programming language, you can treat functions as first-class values. It means, functions can be considered as one of the accepted types to be passed as an argument to other functions or also as return type of other functions.

Eg code snippet where functions are treated as first-class values (as parameters),

def function1(a:Int)={
(a*2)+4
}
List(55,6,6).map(function1) //function1 is used as input argument just as any Int or String literal.

Eg code snippet where function is defined as return type,

def function2():Int=>String={
a=>a.toString
}
function2()(5) // Output is val res13: String = 5.

What happens here is function2() returns a function with skeleton Int=>String ie, a=>a.toString. For that function value, 5 is passed as an input and “5” (Int converted to String) is returned.

2)Methods should not have any side effects,

Understanding side effects is important. Even println can cause side-effects. What is meant by methods without side effects? Methods that communicate only by taking input arguments and mapping those input to output results without any data in-place change. This type of methods are also be termed as referentially transparent methods.

‘Referentially transparent methods’ — Another important keyword in functional language!!

Set of input arguments can directly be replaced with the methods output having a guarantee of not generating any other effect

def function1(a:Int):Int = i+1 // referentially transparent
def function1(a:Int):Int = { println(i); i+1 } // Not referentially transparent
How println makes the function referentially not transparent? Calling, function1(1)+function1(1) The result will be 4 and the output will be,
// 1
// 1
Whereas executing, val a= function1(1); a+a; the output will be different,
Result is same as 4 but the output is,
// 1

Both didn’t end-up in same output. Having referentially transparent methods with replace the input with same output without any side effects

Another example to understand method with side effects better,

var a=5
def func3(x:Int)={
a+x
}
func3(5)// Result is 10. Consider you changing the value of a , var a=3
func3(5)// Result is 8.

You can clearly see the same function call with same argument returns different values. The function func3 execution depends on external variable’s state and that makes it not referentially transparent. .

Having referentially transparent methods help in concurrent programming in Scala (shared nothing model), increased readability, easier testing and debugging etc.

Scala gives the programmer a choice, either to write in imperative style(Stateful programming) or to write in functional style based on the needs.

Now we all know what is functional programming, functions as first class values,side effects/ referential transparency and how it helps.

Let me share few concepts which we use daily but hardly know what it’s,

⦁ Methods without return type are only executed for its side effects. ie, there are methods that don’t return any result , they are actually meant for the side effects they create.

⦁ What is function literal? Function literal are functions created with val keyword and can be used as other literals. These val functions are instances of traits Function0 through Function22 under the hood. There are concrete methods implemented in those traits like toString,compose etc available to be invoked on them.

Consider the function literal,

val functn = (a:Int,b:Int)=> a+ b;functn(5,6).toString; // result is val res1: String = 11. toString is a concrete method available under trait Function2.

The function literal can be passed as an argument or be used as return type just like other literals. This particular function literal is an instance of trait Function2, because the function literal has two arguments passed to it. You can invoke concrete functions like toString etc, available on that particular instances. To know what are all the functions available for each trait from Function0 to Function22, refer this scala official page, https://www.scala-lang.org/api/current/scala/Function1.html. Also, function literals becomes the object itself (function values) at run time.

Similarly while creating tuples (which allows you to have different type of elements in same data structure), you cannot directly use Tuple as collection class to create one. Hence, you cannot invoke methods like filter,map(collection class methods) on tuple elements. for eg,

val a:Tuple[Int,String]=Tuple(1,"trial") // this is wrong. You will get error. val a:Tuple2[Int,String]=Tuple2(1,"trial") // this works
or
val a=(1,"trial") // this also works

Note: there are tricks that surpass this limit (22) on number of arguments on functions and tuples.

⦁ Partially applied functions? Not all arguments are passed in the function call. Only few or none of arguments are passed in function call and can later be used to pass the remaining arguments.

Eg,
def func2(a:Int,b:Int) = a+b
val first=func2(3,_)
first(4) // Result is 7
val second=func2 _
second(3,4) //Result is 7

⦁ Implicit parameters & functions:

While having implicit parameters as part of the arguments passed for a function, the compiler tries to pass values implicitly for those parameters if not explicitly given. Lets see examples to get better idea,

def func4(implicit a:Int,b:String,c:Double)={
println(a,b,c)
}
implicit val first_arg=5
implicit val second_arg="Hello"
implicit val third_arg=9.0
func4 //The output is,
(5,Hello,9.0)

Providing additional implicit values of same expected type confuses compiler and causes error during invocation.

implicit val first_arg=5
implicit val second_arg="Hello"
implicit val third_arg=9.0
implicit val first_copy=6 // Two implicit Int values are available for the first argument
func4 //Error thrown,
error: ambiguous implicit values:

both first_arg and first_copy are of type Int. And the match expected for first argument is Int. And so causes compilation error.

Implicit functions with implicit type conversions,
implicit def func3(a:Int):String ={
println("I am inside function")
"HEllo!! Welcome Home"
}
100.split("!!") //Result is,
I am inside function
val res2: Array[String] = Array(HEllo, " Welcome Home")

What happened above is 100 is of Int type and split is a function that can be applied on String class. So the compiler looks for a function that takes Int value as parameter and returns string that’s needed for applying split. 100 is passed to the function func3 and the string “HEllo!! Welcome Home” value is returned, then split on !! is invoked.

Lets see one other implicit function type conversion but this time not an primitive type conversion.

class trial_class(a:String){
def func1 =println(a)
}
implicit def implicit_function(b:Int):trial_class =new trial_class("Check!! Check!!")
5.func1 //Result is,
Check!! Check!!

Here func1 is invoked on Int class ie,5 but func1 should be invoked on trial_class object. So the compiler looks for function which takes Int value as input and returns trial_class object. And with the trial_class object returned, func1 function is invoked. Implicit functions are mainly invoked based on the input parameters type and the output value type returned, not the function names. As you can see in the above examples, implicit functions names are not relied during function calls.

⦁ Currying a function? A multiple arguments function can be changed to a function with multiple single arguments.

// Function without currying
def mutilplearguments(a:Int,b:Int,c:Int)= {
a+b*c/100 + a*c/100
}
mutilplearguments(5,2,3) // Result is 5
//Curried Function
def mutilplearguments(a:Int)(b:Int)(c:Int)= {
a+b*c/100 + a*c/100
}
// Curried function (partially applied)
val first=mutilplearguments(5)_;
val second = first(2);
val third= second(3); // Result is 5
val first= mutilplearguments(5)(2)_;
val second=first(3); // Result is 5

Practical scenario where currying and partially applied functions be helpful:

Currying and partial applied functions are useful when you dont have all the arguments available during the function call. Also consider you have a function with 3 arguments, two of the arguments are always the same and only third argument gets changed frequently. Then you can use currying which helps to have the function with same two arguments be passed and assigned in a literal. And then the literal can be supplied with the varying third argument whenever required.

--

--

Teepika R M

AWS Certified Big Data Specialty| Linux Certified Kubernetes Application Developer| Hortonworks Certified Spark Developer|Hortonworks Certified Hadoop Developer