Higher-order functions

In Scala's functional programming, you are allowed to pass functions as parameters and even return a function as a result from another function; this defines what are called higher-order functions.

Let's demonstrate this feature by an example. Consider the following function testHOF that takes another function func and then applies this function to its second argument value:

object Test {
def main(args: Array[String]) {
println( testHOF( paramFunc, 10) )
}
def testHOF(func: Int => String, value: Int) = func(value)
def paramFunc[A](x: A) = "[" + x.toString() + "]"
}

After demonstrating the basics of Scala's functional programming, now we are ready to move to more complex cases of functional programming. As mentioned earlier, we can define a higher-order function as a function that accepts other functions as arguments and it returns them as a result. If you are coming from an object-oriented programming background, you will find it very a different approach, but it will become easier to understand as we go on.

Let's start by defining a simple function:

def quarterMaker(value: Int): Double = value.toDouble/4

The previous function is a very simple one. It's a function that accepts an Int value and then returns a quarter of this value in a Double type. Let's define another simple function:

def addTwo(value: Int): Int = value + 2

The second function addTwo is more trivial than the first one. It accepts an Int value and then adds 2 to it. As you can see, these two functions have something in common. Both of them accept Int and return another processed value that we can call AnyVal. Now, let's define a higher-order function that accepts another function among its parameters:

def applyFuncOnRange(begin: Int, end: Int, func: Int => AnyVal): Unit = {
for (i <- begin to end)
println(func(i))
}

As you can see, the preceding function applyFuncOnRange accepts two Int values that work as a beginning and end to a sequence and it accepts a function that has the Int => AnyVal signature just like the previously defined simple functions (quarterMakder and addTwo). Now let's demonstrate our previous higher-order function by passing one of the two simple functions to it as a third argument (if you want to pass your own function then make sure that it has the same signature Int => AnyVal).

Scala syntax for loop with ranges: The simplest syntax of using a for loop with ranges in Scala is:
for( var x <- range ){
statement(s)
}
Here, the range could be a range of numbers and is represented as i to j or sometimes like i until j. The left-arrow operator is called a generator because it's generating individual values from a range. Let's see a concrete example of this feature:
object UsingRangeWithForLoop {
def main(args: Array[String]):Unit= {
var i = 0;
// for loop execution with a range
for( i <- 1 to 10){
println( "Value of i: " + i )
}
}
}
The output of the preceding code is as follows:
Value of i: 1
Value of i: 2
Value of i: 3
Value of i: 4
Value of i: 5
Value of i: 6
Value of i: 7
Value of i: 8
Value of i: 9
Value of i: 10

Let's first define our functions before starting to use them as shown in the following screenshot:

Figure 2: An example of defining a higher-order function in Scala

Now, let's start by calling our higher-order function applyFuncOnRange and passing the quarterMaker function as a third argument:

Figure 3: Calling a higher-order function

We can even apply the other function addTwo since it has the same signature as shown in the following screenshot:

Figure 4: An alternative way of calling a higher-order function

Before going into more examples, let's define what's called a callback function. A callback function is a function that can be passed as an argument to another function. Other functions are simply normal functions. Let's demonstrate more examples of using different callback functions. Consider the following higher-order function, which is responsible for transferring a specific amount of money from your account:

def TransferMoney(money: Double, bankFee: Double => Double): Double = {
money + bankFee(money)
}
def bankFee(amount: Double) = amount * 0.05

After calling the TransferMoney function on 100:

TransferMoney(100, bankFee)

The output of the preceding code is as follows:

105.0

From a functional programming point of view, this code is not ready to be integrated into the banking system because you need to apply different validations on the money parameters, such as it has to be positive and greater than the specific amount specified by the bank. However, here we are just demonstrating the use of high-order functions and callback functions.

So, this example works as follows: you want to transfer a specific amount of money to another bank account or money agent. The bank has a specific fee to be applied depending on the amount that you are transferring and here comes the role of the callback function. It takes the amount of money to transfer and applies the bank fee to it in order to come up with the total amount.

The TransferMoney function takes two parameters: the first one is the money to be transferred and the second one is a callback function with the signature Double => Double that the function applies to the money argument to determine the bank fee over the transferred money.

Figure 5: Calling and giving extra power to the higher-order function

The complete source code of the preceding examples can be seen as follows (we called the methods using some real values):

package com.chapter3.ScalaFP
object HigherOrderFunction {
def quarterMaker(value: Int): Double = value.toDouble / 4
def testHOF(func: Int => String, value: Int) = func(value)
def paramFunc[A](x: A) = "[" + x.toString() + "]"
def addTwo(value: Int): Int = value + 2
def applyFuncOnRange(begin: Int, end: Int, func: Int => AnyVal): Unit = {
for (i <- begin to end)
println(func(i))
}
def transferMoney(money: Double, bankFee: Double => Double): Double = {
money + bankFee(money)
}
def bankFee(amount: Double) = amount * 0.05
def main(args: Array[String]) {
//Now call all the methods with some real values
println(testHOF(paramFunc, 10)) // Prints [10]
println(quarterMaker(20)) // Prints 5.0
println(paramFunc(100)) //Prints [100]
println(addTwo(90)) // Prints 92
println(applyFuncOnRange(1, 20, addTwo)) // Prints 3 to 22 and ()
println(TransferMoney(105.0, bankFee)) //prints 110.25
}
}

The output of the preceding code is as follows:

[10] 
5.0
[100]
92
3 4 5 6 7 8 9 10 11 12 13 14 15 16 1718 19 20 21 22 ()
110.25

By using callback functions, you are giving extra power to the higher-order function; so, it's a very powerful mechanism to make your program more elegant, flexible, and efficient.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.20.133