In Java (Java 7 and before) we can store an object reference in a variable or some primitive value in which case Classes and Primitive types are the first class citizens there, but in Scala we can assign method/function definitions to variables, we can pass around function definitions to other functions and we can even declare functions within fuctions (i.e private functions).
also read:
In this small article I will show you how to:
- Assign functions to variables also called Function Values
- Pass functions as arguments to other functions. The functions which accept other functions as parameters are called Higher Order Functions.
- Parameter Inference while using Higher Order functions
- Currying- which in short is a function which returns another function
- Some common higher order functions in Scala collections API
Function Values
Lets see how we can assign a function definition to variable:
scala> val doubleNumber = (x:Int) => x * 2 doubleNumber: (Int) => Int =
The above code was executed in the REPL shell (Read Evaluate Print Loop) which can be accessed by running the command: scala. We can see that the variable doubleNumber is assigned a function which takes an Int and returns another Int. Anything to the left of => are arguments to the function block and anything to the right of => is the function block which would use the values passed as parameters or can even access values defined outside the block (in which case the function is said to close over that variable also called as closures). Invoking the above method is simple:
scala> println(doubleNumber(7)) 14
We can also make use of the variables declared outside of the function block, in which case the function value becomes a closure. Such a way of referring to the variables from outside the function block is really powerful. Java programmers are aware of Anonymous Inner classes not able to capture the variables outside of their class definition other than the variable which are declared as final. This restriction is being removed in Java 8.
scala> val increaseByAFactor = (x: Int) => x * factor :5: error: not found: value factor val increaseByAFactor = (x: Int) => x * factor ^
the error above is because the function value refers to factor which is not declared before.
scala> var factor = 3 factor: Int = 3 scala> val increaseByAFactor = (x: Int) => x * factor increaseByAFactor: (Int) => Int =
now invoking the function value we have:
scala> println(increaseByAFactor(4)) 12
We can even update the function value to take in two parameters- one the value to increment and the other the factor by which to increment.
scala> val increaseByFactor = (x:Int, factor:Int) => x * factor increaseByFactor: (Int, Int) => Int = scala> println(increaseByFactor(4,2)) 8
Another term associated with Function Value is Function Literal. The function: (x:Int, factor:Int) => x * factor is called as function literal and when assigned to a variable these are together called function values.
Lets dig into how scala compiler handles these function values. For that we would have to compile the above code into bytecode:
object Main extends Application{ val doubleNumber = (x:Int, factor:Int) => 2 * factor println(doubleNumber(2,3)) }
Once compiled we would have: Main$$anonfun$1.class, Main$.class, Main.class. Inspecting Main.class one of the entries there is:
public static final scala.Function2 doubleNumber();
which shows that the doubleNumber function value is actually converted to a method call doubleNumber() which returns an instance of Function2 class. The Function2 is for function values which take in 2 parameter, if the function value takes one parameter then Function1 instance is created. These FunctionN classes have an apply method which takes these 2 parameters and then perform the operation specified in the function literal.
Higher Order Functions
We would declare a function which takes 2 integer- the lower limit and higher limit and another function which is applied to each integer between the lower and higher integers. Such a function we call it as Higher Order Function because it takes in other function as a parameter or can return another function as a return value.
scala> def performOnRange(low:Int, high:Int, func:Int=>Int){ | low.to(high).foreach(x=> println(func(x))) | } performOnRange: (low: Int,high: Int,func: (Int) => Int)Unit
Here performOnRange is a higher order function. We can define a function value which would be passed to performOnRange:
scala> var factor = 5 factor: Int = 5 scala> val incrByFact = (x:Int) => x*factor incrByFact: (Int) => Int =
and then invoke the performOnRange as below:
scala> performOnRange(1,5,incrByFact) 5 10 15 20 25
We could have directly passed the function literal to the performOnRange, something like:
scala> performOnRange(1,5, (x:Int) => x * factor) 5 10 15 20 25
which can be further shortened to:
scala> performOnRange(1,5, _ * factor) 5 10 15 20 25
where each _ stands for a different parameter. In this case we had only on parameter to the function literal and hence only one _.
Common Higher Order Functions in Scala collections
Scala collections API supports the use of higher order functions for operating on the collection data and what’s good about this is that the call to the to APIs can be chained something like collection.method1(function1).method2(function2). A good news for Java developers is that the same feature is being implemented in Java 8 collections API.
Lets look at few examples:
Finding out the highest number from the array of numbers:
scala> var numbers = List(3,4,561,2,587,34,23) numbers: List[Int] = List(3, 4, 561, 2, 587, 34, 23) scala> numbers.reduceLeft((x:Int,y:Int) => if ( x < y ) y else x) res6: Int = 587
reduceLeft applies the function passed for each element of the array from the left i.e ((((((3,4),561),2),587),34),23). we can make use of parameter inference and then skip the type information for the parameters as:
scala> numbers.reduceLeft((x,y) => if ( x < y ) y else x) res7: Int = 587
Lets look at a way to increment each number in the collection and return a new collection instead of editing the original collection:
scala> numbers.map(x => x +10) res8: List[Int] = List(13, 14, 571, 12, 597, 44, 33) scala> println(numbers) List(3, 4, 561, 2, 587, 34, 23)
the original collection remains the same.
scala> numbers.map(_+10) res11: List[Int] = List(13, 14, 571, 12, 597, 44, 33)
we can also omit the parameters for the function value and then replace it with _
Finding factorial using foldLeft:
scala> 1.to(5).foldLeft(1)((x,y) => x * y) res23: Int = 120
the foldLeft is exactly similar to reduceLeft but the only difference is that it takes an initial value and then uses it to apply the function passed.
Currying
Currying transforms a function that takes multiple parameters into a chain of functions each taking a single parameter. We have seen increaseByFactor taking 2 parameters- the value to be incremented and the factor by which to increment. The curried version of the same would be:
scala> def curriedIncrement(factor:Int) = (x:Int) => factor * x curriedIncrement: (factor: Int)(Int) => Int scala> val incrementBy5 = curriedIncrement(5) incrementBy5: (Int) => Int = scala> println(incrementBy5(10)) 50 scala> val incrementBy2 = curriedIncrement(2) incrementBy2: (Int) => Int = scala> println(incrementBy2(10)) 20
In the above example the curriedIncrement takes in a factor parameter and returns another function with the factor parameter curried into it (something like half cooked function) and then we can create multiple instance of curriedIncrement by passing in different factor values.
We already saw currying being used in the above example when we used foldLeft:
scala> 1.to(5).foldLeft(1)((x,y) => x * y) res23: Int = 120
These are some of the basic concepts which would help any Java programmer to get started with the Functional concepts of Scala.