Higher Order functions

Scala allows you to create Higher Order functions. These are the functions that can do at least one of following things.

  1. It can take a function as an argument
  2. It can return a function

In this article, we will try to understand the syntactical side of the following things.

  1. How to create a function that takes another function as an argument
  2. How to create a function that returns another function

How to create a function that takes another function as an argument?

Let's try to understand it with the help of a simple example.
We want to create an integer decorator function. It should take two arguments. The first argument should be an integer and the second argument should be the logic to decorate the given integer.
The following example shows some usages and the result.

                                        
    intDecorator(5, (y:Int) => "[" + y + "]" )
    //res3: String = [5]
    intDecorator(5, (y:Int) => "-" + y + "-" )
    //res4: String = -5-
    intDecorator(5, (y:Int) => "Page " + y + "-" )
    //res5: String = Page 5-  
                                    

The first example takes five and returns the same in a bracket. The second example takes five and decorates it with a hyphen. The third example also does a similar thing. Can you define intDecorator?
Let's take another requirement. Create a function sumOfX. It takes three inputs. The first two parameters are integers. The last parameter is the logic to create the sum. The following example shows some usages and the result.

                                        
    sumOfX(3,5, (x,y)=> x+y)
    //res7: Int = 8
    sumOfX(3,5, (x,y)=> x*x +y*y)
    //res8: Int = 34
    sumOfX(3,5, (x,y)=> x*x*x +y*y*y)
    //res9: Int = 152  
                                    

The first example takes 3 and 5 as first two inputs and returns a simple sum. The second example takes 3 and 5 as first two inputs and returns the sum of squares. Similarly, the last example returns a sum of cubes. The code below shows the definition of sumOfX as well as the intDecorator.

                                        
    def sumOfX(x:Int, y:Int, f:(Int,Int) => Int) = f(x,y)
    def intDecorator(x:Int, f: Int=>String) = f(x)  
                                    

The functions shown in the above example takes a function value as an input argument. There is no complexity in the syntax as long as you pass simple parameters. Every parameter has three parts.

  1. Parameter name (for example x)
  2. A Colon ( : )
  3. Value type (for example Int)

The function parameter syntax is also same.

  1. Parameter name (for example f )
  2. A Colon ( : )
  3. Function value type for example (Int, Int) => Int

Defining a function value type is all about knowing the input type and output type of the target function. So we define the type of the function value f as below.

  1. A right arrow in the middle
  2. Input type on the left
  3. Output type on the right

It simply tells that the function f takes two Integers and gives a single Integer. Defining a Higher Order function that takes another function is as simple as shown in the above examples.


How to create a function that returns another function?

We created a sumOfX function earlier. The earlier version of the sumOfX takes three arguments. The first two arguments were integers, and the final argument was the logic to calculate the sum. Let's create another version of the same.

                                        
    def sumOfX(f:(Int,Int)=>Int) = {
        def myLocalFunc(x:Int, y:Int) = f(x,y)
        println("I am returning a function")  
        myLocalFunc _
    }            
    //sumOfX: (f: (Int, Int) => Int)(Int, Int) => Int  
                                    

This new version takes a single argument. It takes only the logic to calculate the sum.
In the function body, We create a local function as myLocalFunc, and then we return it as the last expression of the sumOfX function. As a result, the sumOfX is now a Higher Order function. The returned function takes two integers and calculates the sum based on the logic provided to the sumOfX. Primarily, I tried to break it into a two-step process. Now you can use it as shown below.

                                        
    //Step 1 - Provide the logic
    val simpleSum = sumOfX((x,y)=> x+y)
    /* Output: -
    I am returning a function
    simpleSum: (Int, Int) => Int = <function2>
    */
    //Step 2 - Canculate simple sums
    simpleSum(3, 5)
    //res10: Int = 8
    -----------------------------
    //Step 1 - Provide the logic
    val sumOfSquare = sumOfX((x,y)=> x*x+y*y)
    /* Output: -
    I am returning a function
    sumOfSquare: (Int, Int) => Int = <function2>
    */
    //Step 2 - Canculate sum of squares
    sumOfSquare(3, 5)
    //res11: Int = 34  
                                    

The examples shown above is the primary method to create a Higher Order function that returns another function. However, we can apply some shortcuts to squeeze the code.
Instead of declaring a local function and then returning in the end, we can directly use an inline function literal at the end. The example below shows that change.

                                        
    def sumOfX(f:(Int,Int)=>Int) = {
        println("I am returning a function")  
        (x:Int, y:Int) => f(x,y)
    }     
                                    

The last line of the sumOfX is a function literal. As we already learned in the function literals, it contains two parts saperated by an => symbol. A list of parameters comes before the => symbol and the function body after the => symbol.
You will often see another syntactic sugar. You can shift the list of parameters to the beginning, right after the = symbol of the parent function. Here is the code.

                                        
    def sumOfX(f:(Int,Int)=>Int) =  (x:Int, y:Int) => {
        println("I am returning a function")  
        f(x,y)
    }     
                                    

The syntax shown in the above example is more common and popular. However, it is just a syntactic sugar of returning a local function from a Higher Order function. I hope this helps you to grab the syntax for creating Higher Order functions in Scala.


Read More

Basics of Scala functions | Function Literals in Scala | Function values | Local Functions | Variable length argument | Default values and named arguments | Scala Placeholder syntax | Higher Order functions | Partially applied functions | Function currying

By Prashant Pandey -


You will also like:


Pattern Matching

Scala takes the credit to bring pattern matching to the center.

Learning Journal

Anonymous Functions

Learn Scala Anonymous Functions with suitable examples.

Learning Journal

Referential Transparency

Referential Transparency is an easy method to verify the purity of a function.

Learning Journal

Statements and Expressions

Statements and Expressions in Scala. How are they different?

Learning Journal

Lazy Evaluations

Evaluate the expression now vs evaluate it for the first use. Strict vs Lazy?

Learning Journal