23.1 Introduction

Functions in Scala are part of the type system. That is, they are part of the system of entities that comprise the types that variables and parameters can reference. Thus just as a parameter can be of type Int, or Boolean, or Person a parameter to a method or a function can be another functions. A function that takes a function as a parameter is referred to as a higher-order function. This chapter discusses higher-order functions and how they can be used and provides some examples taken from the collection classes in Scala.

23.2 Higher-Order Function Concepts

In Scala higher-order functions are functions that do at least one of the following (and may do both):

  • Take as a parameter one or more functions

  • Return as output a function

All other functions in Scala are first-order functions.

Many of the functions found in the collection classes such as map, foldLeft, foldRight and forEach are higher order functions. It is a common enough pattern that once you are aware of it you will recognise it in many different classes and objects.

As an abstract example, consider the following higher-order function apply. This function (written in pseudo code) takes an integer and a function. Within the body of the function being defined the function passed in as a parameter is applied to the integer parameter. The parameter function takes an integer and returns a Double. The result of the function being defined is then returned:

$$ \begin{array}{*{20}l} &{ {\text{define}}\;{\text{apply}}\left( {{\text{x}}:{\text{Int}},{\text{func}}:\left( {\text{Int}} \right) = > {\text{Double}}} \right):{\text{Double}}} \hfill \\ & {\text{begin}} \hfill \\ & { \;\;{\text{result}} = {\text{func}}\left( {\text{x}} \right)} \hfill \\ & { {\text{return}}\;{\text{result}}} \hfill \\ & {\text{end}} \hfill \\ \end{array} $$

The function apply is a higher-order function because its behaviour (and its result) will depend on the behaviour defined by another function—the one passed into it.

We could also define a function that multiplies an integer by 10.0, for example:

$$ \begin{aligned}& {\text{define}}\;{\text{mult}}\left( {{\text{y}}:{\text{Int}}} \right):{\text{Double}} \\ & {\text{begin}} \\ & \;\;{\text{return}}\;{\text{y}}*10.0 \\ & {\text{end}} \\ \end{aligned} $$

Now we can use the function mult with the function apply, for example,

$$ {\text{apply}}( 5,{\text{mult}}) $$

This would return the value 50.0.

23.3 Scala Higher-Order Functions

The key to understanding how higher-order functions are defined is to understand that a function definition defines a function type. That is a type that takes Zero or a set of parameters (of given types) and returns a result. Thus the function:

$$ {\texttt{(x:Int)\,=>\,x\,*\,2}} $$

Is a one parameter function that takes an Int and returns a Int—that is its type. Thus the

$$ {\texttt{(Parameter\,type\,list)\,=>\,return\,type}} $$

Defines a new Function type.

Thus a parameter that expects to be given a reference to a function that takes an Int and returns an Int can be given a reference to any function that takes an Int and returns an Int. All of the following functions meet that criteria

All of the above could be used with the following higher-order function:

For example,

The output from this application is:

100

This is actually also true for methods. For example the following is a higher-order method, where the second parameter is a function:

For example:

$$ {\texttt{println}}({\texttt{Processor}}{\texttt{.apply}}({\texttt{10}},{\texttt{f1)}} $$

Notice that as previously mentioned, from the invoking clients point of view there is little different between a function and a method.

The following listing provides a complete set of the earlier sample functions and how they may be used with the apply function:

The output from this program is:

23.4 Using Higher-Order Functions

Looking at the previous section you may be wondering why you would want to use a higher-order function or indeed why define one. After all could you not have called one of the functions (f1 to f4) directly by passing in the integer to used? Yes we could have, for example we could have done:

$$ {\text{f}}4(10) $$

And this would have exactly the same effect as calling:

The first approach would seem to be both simpler and more efficient.

The key to why higher-order functions are so powerful is to consider what would happen if we know that some function should be applied to the value 10 but we do not yet know what it is. The actual function will be provided at some point in the future. Now we are creating a reusable piece of code that will be able to apply an appropriate function to the data we have when that function is known.

For example, let us assume that we have a Person class. This person class has a salary property. However, we do not know how to calculate the tax that this person must pay as it is dependent on external factors. The calculateTax method could take an appropriate function that performs that calculation and provides the appropriate tax value to be stored.

The following listing implements this approach. The class Person itself does not have a way of calculating the tax; this must be provided as a parameter to the calculateTax method. The function passed in takes a Double and returns a Double. It is used with the salary property to determine the taxToPay property.

The TestPerson object defines a new function that takes a double and multiplies it by 0.3 and then uses the Math.ceil function to round it up to a whole number. This function is stored into the taxCalculator local val variable. It then creates a new instance of the Person class specifying a salary of 45,000. The calculateTax method is called passing in the taxCalculator function. Finally it prints out the tax calculated for the person. The result of running this program is:

Thus the class Person is a reusable class that can have different tax calculation strategies defined for it.

23.5 Higher-Order Functions in Scala Collections

The place that you are most likely to encounter higher-order functions in Scala, at least when learning Scala, is within the Scala collection classes. Two examples are the filter method and the foreach method. These are both higher-order functions defined on the Class List . A List is an ordered sequence of values (and will be discussed in greater detail later in this book).

The definition of the filter method is:

That is, the method takes a single parameter. This parameter is a type of function that takes a single parameter and returns a Boolean. The whole method (filter) itself returns a new List. The ‘A’ in the above is a placeholder for the type held by the List to which filter will be applied (i.e. a list of Strings, or a List of Ints).

The filter method selects all elements of the list that satisfy the predicate function p . The function p is used to test all the elements in the List. Those that return true are included in the list of values returned. The result returned is a new list consisting of all elements of this list that satisfy the given predicate p. The order of the elements is preserved.

The definition of the foreach method is:

The foreach method applies a function ‘f’ to all elements of the list in turn. Any result generated by the function ‘f’ is discarded.

The following listing illustrates creating a list and applying simple functions to filter and foreach . Note the examples illustrate the different ways in which the functions to be applied can be defined: