Extensibility with Higher-Order Functions

Functions that can take other functions as parameters are called higher-order functions. They reduce code duplication, increase reuse, and make code concise as well. We can create functions within functions, assign them to references, and pass them around to other functions. Scala internally deals with these so-called function values by creating them as instances of special classes. In Scala, function values are really objects.

Let’s rewrite the previous example using function values. With this new version, we can perform different operations, such as summing numbers or counting the number of even numbers on a range of values.

We’ll start by first extracting the common code into a method named totalResultOverRange, by looping over the range of values:

 
def​ totalResultOverRange(number: ​Int​, codeBlock: ​Int​ => ​Int​) = {
 
var​ result = 0
 
for​ (i <- 1 to number) {
 
result += codeBlock(i)
 
}
 
result
 
}

We’ve defined two parameters for the method totalResultOverRange. The first one is an Int for the range of values to iterate over. The second one is special; it’s a function value. The name of the parameter is codeBlock, and its type is a function that accepts an Int and returns an Int. The result of the method totalResultOverRange is itself an Int.

The symbol => specifies and separates the function’s expected input to the left and the expected return type to the right. The syntax form input => output is intended to help us think of a function as transforming input to output without having any side effects.

In the body of the totalResultOverRange method we iterate over the range of values, and for each element we invoke the given function, referenced by the variable codeBlock. The given function is expected to receive an Int, representing an element in the range, and return an Int as a result of computation on that element. The computation or operation itself is left to be defined by the caller of the method totalResultOverRange. We total the results of calls to the given function value and return that total.

The code in totalResultOverRange removed the duplication from the example in Limitations of Normal Functions. Here is how we’d call that method to get the sum of values in the range:

 
println(totalResultOverRange(11, i => i))

We passed two arguments to the method. The first argument is the upper limit (11) of the range we want to iterate over. The second argument is actually an anonymous just-in-time function—that is, a function with no name but only parameter(s) and an implementation. The implementation, in this example, simply returns the given parameter. The symbol =>, in this case, separates the parameter list on the left from the implementation on the right. Scala was able to infer that the type of the parameter, i, is an Int from the parameter list of totalResultOverRange. Scala will give us an error if the parameter’s type or the result type does not match with what’s expected.

For a simple totaling of the values, it may appear that the call to totalResultOverRange with a number and a function as arguments was rather cumbersome, compared to the call to the normal function sum we wrote earlier. However, the new version is extensible and we can call it in a similar way for other operations. For example, instead of finding the sum, if we’d like to total the even numbers in the range, we’d call the function like this:

 
println(totalResultOverRange(11, i => ​if​ (i % 2 == 0) i ​else​ 0))

The function value we pass as argument in this case returns the input parameter if it is even; otherwise it returns a 0. Thus, the function totalResultOverRange will only total even values in the given range.

If we’d like to total the odd numbers, we can call the function as follows:

 
println(totalResultOverRange(11, i => ​if​ (i % 2 != 0) i ​else​ 0))

Unlike the sum function, we saw how the totalResultOverRange function can be extended to perform a total for a different selection of values in the range. This is a direct benefit of the indirection we achieved using higher-order functions.

Any number of parameters of a function or a method can be function values, and they can be any parameter, not just the trailing parameter.

It was quite easy to make the code DRY (see The Pragmatic Programmer: From Journeyman to Master [HT00] by Andy Hunt and David Thomas for details about the Don’t Repeat Yourself [DRY] principle) using function values. We gathered up the common code into a function, and the differences were rolled into arguments of method calls. Functions and methods that accept function values are commonplace in the Scala library, as we’ll see in Chapter 8, Collections. Scala makes it easy to pass multiple parameters to function values, and define the types of arguments as well, if you desire.

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

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