100+ FAQ Scala interview questions answered with code. Learn more about FP – 6 tips to transforming your thinking from OOP/imperative programming to functional programming (i.e. FP). Scala is very popular in Apache Spark programming to process Big Data. 70+ FAQ Bigdata & Hadoop interview questions & answers with diagrams & tutorials.
Q1. What is a function in Scala?
A1. A function is a group of expressions that perform a task. A Scala function declaration is of the form:
1 2 3 4 5 6 |
def functionName ([list of parameters]) : [return type] = { function body return [expr] } |
Scala simple function
“addOne” is a function that takes an “Int” and returns a result of type “Int”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
object FunctionsInScala3 extends App { // a function that takes an argument of Int type // and returns a result of Int type def addOne(x: Int): Int = { x + 1 //returns an int } //calls the above function val result = addOne(5); //returns 6 print(result) } |
Q. What is an “object” in Scala?
A. In Scala class defines a Class, object defines a Singleton object. It can be used to hold static members that are not associated with instances of some class.
This is same as above named function addOne(…), but as an anonymous class, which is an instance of ‘Function1’
1 2 3 |
trait Function1[-T1, +R] extends AnyRef |
T1 is input argument of type T1, which can be Int, String, Person, etc, and R is a return type of R, which can be of type Int, String, Person, etc. Similarly, trait Function2, where T1 and T2 are inputs of types T1 & T2, whereas R is a return type of R.
1 2 3 |
trait Function2[-T1, -T2, +R] extends AnyRef |
Used as shown below:
1 2 3 4 5 6 7 8 9 10 11 12 |
object FunctionalScala3 extends App { //anonymous class assigned to variable "addOne" val addOne = new Function1[Int, Int] { def apply(x: Int): Int = x + 1 } //calls the above function val result = addOne(5); //returns 6 print(result) } |
“addOne(5)” is same as “addOne.apply(5)” OR as “addOne apply 5 ”
So, all below are same in Scala:
1 2 3 |
val result = addOne(5) //returns 6 |
OR
1 2 3 |
val result = addOne.apply(5) //returns 6 |
OR
1 2 3 |
val result = addOne apply 5 //returns 6 |
Similarly,
1) Function1[Int, Int] is same as Int => Int i.e. A function that takes one arg of type Int and returns a result of type Int.
2) Function2[Int, Int, Int] is same as (Int , Int) => Int i.e. A function that takes two arguments of type Int and returns a result of type Int.
….
3) Function5[Int, Int, Int, Int, Int, Int] is same as (Int , Int, Int, Int, Int) => Int i.e. A function that takes 5 arguments of type Int and returns a result of type Int.
3) Function3[Int, String, Person, Person] is same as (Int , String, Person) => Person i.e. A function that takes 3 arguments of types Int, String & Person respectively and returns a result of type Person.
The above example implicitly infers the return type for the variables addOne & result values. The below code shows them explicitly after the “:”
1 2 3 4 5 6 7 8 9 10 11 12 |
object FunctionalScala3 extends App { //anonymous class assigned to variable "addOne" val addOne: Int => Int = new Function1[Int, Int] { // return type is Function1 i.e. Int => Int def apply(x: Int): Int = x + 1 } //calls the above function val result: Int = addOne.apply(5) //returns 6. // return type is Int print(result) } |
“abstract def apply(v1: T1): R” meaning Apply the body of this function to the argument. This is why you don’t have to define a list with a “new” keyword in Scala as most Scala objects have this “apply” function. You can just do
1 |
val list = List(1,2,3,4) |
which is same as writing:
1 |
val list = List.apply(1,2,3,4) |
the apply method is like a constructor which takes arguments and creates an object. The unapply takes an object and tries to give back the arguments. This is useful in Scala pattern matching.
apply vs unapply
1 2 3 4 5 6 7 8 9 10 11 |
import scala.util.Random object UserId { def apply(name: String) = s"$name-${Random.nextLong}" def unapply(userId: String): Option[String] = { val stringArray: Array[String] = userId.split("-") if (stringArray.tail.nonEmpty) Some(stringArray.head) else None } } |
Name the above file as “UserId.scala“. Bring up the Scala REPL with:
Compile & Run:
1 |
$ scalac UserId.scala |
1 |
$ scala UserId |
Bring up a REPL:
1 |
$ scala |
Use “:paste” to paste multi-line and “ctrl-D” to finish
1 2 3 4 5 6 7 8 9 10 11 12 13 |
scala> :paste // Entering paste mode (ctrl-D to finish) val userId = UserId("John") // new keyword is not required. // same as UserId.apply("John") //unapply userId (i.e. extractor) userId match { case UserId(name) => println(name) case _ => println("Not found") } // Exiting paste mode, now interpreting. |
Outputs:
1 2 |
John userId: String = John-7544893590134328003 |
In FP “no statements, only expressions“
Q. What is the difference between a statement & an expression?
A. An expression evaluates to a value. A statement does something. Statements represent an action or command e.g print statements, assignment statements. Expression is a combination of variables, operations and values that yields a result value.
Statement
1 2 3 4 5 6 |
if (map contains x ) { map(x) = evalNewX(map(x)) } else { map(x) = evalNewX(0) } |
Expression
1 2 3 4 5 6 |
map(x) = if (map contains x ) { evalNewX(map(x)) } else { evalNewX(0) } |
In Scala, you write it as
1 2 |
map(x) = evalNewX(map.getOrElse(x, 0)) |
In functional languages, there are no statements, only expressions. The addOne function definition code can be rewritten as following using “Int => Int” lambda expression. Takes an “Int” as arg and the body (i.e. x+1) returns an “Int ” as a result.
1 2 3 4 5 6 7 8 9 10 11 |
object FunctionalScala3 extends App { //Expression assigned to "addOne" val addOne = (x: Int) => x + 1 //calls the above function val result = addOne(5); // same as val result = addOne.apply(5); print(result) // returns 6 } |
Anonymous(i.e. NO NAME) lambda expression can be assigned to a variable “addOne”. Expression can also be written as:
1 2 3 |
val addOne = (x: Int) => {x + 1} |
OR as
1 2 3 4 |
//Expression assigned to "addOne" val addOne:Int => Int = (x: Int) => {x + 1} |
So, defining your functions without giving them a name, and sprinkling your code with these types of “function literals” is similar to writing integer literals like 32.
In FP, functions can not only be assigned to variables, but also can be passed around like objects.
Functions are Objects, and Objects are Functions
Q2. Can you explain the statement that “a function” is an object in Scala?
A2. In Scala, everything is an Object. No primitives, no wrapper types, no auto boxing & unboxing, etc. Since, Scala is an Object Oriented Programming (OOP) & a functional programming (FP) language, “Functions are Objects“, and also “Objects are Functions” as every value is an Object in Scala.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
object FunctionsInScala extends App { def addOne(x: Int): Int = { x + 1 //returns an int } def applyAndAddOne(f: Function1[Int, Int], number:Int ) : Int = { f(number) + 1 } //calls the above function val result = addOne(5); //returns 6 println("result = " + result) val result2 = applyAndAddOne(addOne, 5) println("result2 = " + result2) //returns 7 } |
Output:
1 2 3 4 |
result = 6 result2 = 7 |
The function “applyAndAddOne” takes a function as an argument. Since a function is an object, it can be passed around as an argument to other functions. It takes two arguments:
1) A function of type Functio1[Int, Int], which takes an Int as arg and returns an Int type.
2) A “number” of type “Int”.
We are passing the “addOne” function to “applyAndAddOne” function along with a value of 5. So, it invokes the “addOne” function first to add 1 to 5, and then adds another one to return a result of 7.
Functions that take other functions as parameters, or whose result is a function is known as higher-order functions.
Q3. In Scala, why do you have traits like Function1, Function2, Function3, etc up to Function22?
A3. A “trait” in Scala is like an interface in Java. A “trait” has more ways to be applied than an interface in Java. In the above example we used “Function1[Int, Int]” to take a single argument of type “Int” and return a type of Int.
Q. How will you declare a function that takes 3 arguments of type Int, and returns a result of type Boolean?
A. Function3[Int, Int, Int, Boolean]
When you declare something like the following,
1 2 3 |
(arg1:Int, arg2:Int, arg3:Int) => Boolean |
The Scala compiler implicitly creates an implementation of the right function trait. E.g. Function3[Int, Int, Int, Boolean]
Q. How will you declare a function that takes 2 arguments of type Int, 2 arguments of type String and returns a result of type Boolean?
A. Function4[Int, Int, String, String, Boolean].
Q4. Can you explain the following code, and what will be the output?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package com.mytutorial object FunctionsInScala2 extends App { val listOfNumbers:List[Int] = (1 to 10).toList applyFilter( x => x % 2 != 0, listOfNumbers, "odd numbers"); applyFilter( x => x % 2 == 0, listOfNumbers, "even numbers"); applyFilter( x => x % 5 == 0, listOfNumbers, "multiples of 5"); def applyFilter(f: Int => Boolean, input:List[Int], comment:String) = { val result = input.filter(f); println(comment + " = " + result); } } |
A4. The output will be
1 2 3 4 5 |
odd numbers = List(1, 3, 5, 7, 9) even numbers = List(2, 4, 6, 8, 10) multiples of 5 = List(5, 10) |
1) “x => x % 2 != 0” is a function of the form “Int => Boolean”. The Scala compiler will implicitly create an object implementation of the trait “Function1[Int, Boolean]“. This is known as an “Anonymous Function “.
2) “applyFilter” is a function that takes three input parameters 1) a function, 2) a list, 3) a string and returns nothing.
3) “val result = input.filter(f);” filters the list based on the predicate supplied as a function. If the function returns “true”, that element is added to the result. “x % 2 != 0” returns true for the odd numbers, hence odd numbers are added to the list and so on for the even numbers, and multiples of 5.
Q5. Is there anything not quite right with the following Scala code?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package com.mytutorial object FunctionalScala extends App { def addNumbers(input:List[Int]): Int = { var sum = 0; input.foreach(x => sum += x) sum; } def addOddNumbers(input:List[Int]): Int = { var sum = 0; input.foreach(x => if(x % 2 != 0)sum += x) return sum; } def addGt3Numbers(input:List[Int]): Int = { var sum = 0; input.foreach(x => if(x > 3) sum += x) sum; } println(addNumbers((1 to 6).toList)) println(addOddNumbers((1 to 6).toList)) println(addGt3Numbers((1 to 6).toList)) } |
A5. Lacks code reuse. The condition as to what numbers to be added is known as a “predicate“. This predicate can be added as a function and passed to single “addNumbers” function as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package com.mytutorial object FunctionalScala extends App { def addNumbers(input:List[Int], predicate: Int => Boolean): Int = { var sum = 0; input.foreach(x => if (predicate(x)) sum += x) sum; } println(addNumbers((1 to 6).toList, x => true)) println(addNumbers((1 to 6).toList, x => x % 2 != 0)) println(addNumbers((1 to 6).toList, x => x > 3)) } |
Q6. In the “AddGivenNumberExample.scala” below
1 2 3 4 5 6 7 8 9 10 |
object AddGivenNumberExample extends App { val three = new AddGivenNumber(3); println(three.add(5)) //8 } class AddGivenNumber (initialNumber: Int) { def add(numberToAdd: Int) = initialNumber + numberToAdd } |
How can you modify the code in “AddGivenNumber” so that in “AddGivenNumberExample” object instead of “three.add(5)”, you can just do “three(5)“.
A6. Change the name of “add(….)” function in the class “AddGivenNumber” to “apply(….)” as shown below. In mathematics and computer science, Apply is a function that applies functions to arguments. “apply” is a method which get’s called on Function application
So, you can use it in a Scala object as “three.apply(5)” or as three(5) as shown below.
1 2 3 4 5 6 7 8 9 10 11 |
object AddGivenNumberExample extends App { val three = new AddGivenNumber(3); println(three(5)) // can also do three.apply(8). outputs = 8 } class AddGivenNumber (initialNumber: Int) { def apply(numberToAdd: Int) = initialNumber + numberToAdd } |
So, this is same as instead of doing something like the following in other languages
1 2 3 |
val myList = List.instanceOf(3 , 4, 5); |
In Scala, you can do
1 2 3 |
val myList = List.apply(3,4,5) |
as the List object has an “apply” function as shown below. “A*” means type “A“, which can be an Int, String, Boolean, Any, etc and “*” repeated anynumber of times. E.g. Int, Int, Int as in (3, 5, 7).
1 2 3 |
def apply[A](xs: A*): List[A] |
This enables us to create a list in Scala as shown below, and the “apply(…)” method is implicitly invoked.
1 2 3 |
val myList = List(3,4,5) |
Note: Always have the Scala API doco handy. E.g. http://www.scala-lang.org/api. If you look at the Scala APIs, you will see “apply” methods. E.g. Future object, Promise object, List object, and the list goes on.