Higher Order Function

In the earlier section, I explained pure functions. Higher Order function is another concept closely associated with the first class functions. You can define a Higher Order function as given below.
A function that does at least one of the following is a Higher Order Function.

  1. Takes one or more functions as arguments
  2. Returns a function as its result

Let's assume I have a function F1. By default, F1 is a first-class function. So I should be able to pass F1 to another function. Now, let's assume I have another function F2. If we design F2 in a way that it can take F1 as an argument, the F2 is a higher order function. That is what the definition says.
Now, let's look at a working example for both types of Higer Order Functions.

Scala function that takes another function as argument

                                        
    def doubler(i:Int)  =  i * 2
    def tripler(i:Int)  =  i * 3
    def applyF(f: Int => Int, x:Int) = f(x)
    applyF(doubler, 5) 
    //Output:- res0: Int = 10
    applyF(tripler, 5) 
    //Output:- res1: Int = 15                
                                    

The first and second line of the above code defines two Scala functions. The third line defines a higher order function. The function applyF takes two arguments. First argument type is a function and the second argument is an integer. The next line passes doubler function to applyF. Similarly, the last line also passes a tripler function to applyF.


Scala function that returns another function

                                        
    def getOps(c:Int) = {
        def doubler(x:Int) =  x * 2 
        def tripler(x:Int) =  x * 3 
        if(c > 0)   
            doubler _
        else  
            tripler _
        }
        val d = getOps(2)
        d(2)
        //Output:- res6: Int = 4                 
                                    

The code shown above defines a function getOps. The first line inside the body of the getOps defines a local function doubler. The second line defines another local function tripler. Finally, the if expression returns an appropriate local function depending on the value of c. Later, we call the getOps function and assign the returned value to d. The variable d holds a function that we call as the last line. We have seen both types of Higher Order functions.

Why Higher Order Functions?

We can pass functions and return them. However, you might be wondering about the benefits. What is the purpose of all this? I am more than happy to use Object Oriented techniques. What is so great about the notion of passing around functions?


Abstraction

Abstraction is the main benefit of Higher Order functions. Abstraction makes the application easily extendable. When developing with a higher level of abstraction, you communicate the behavior and less the implementation. Let's try to understand it with a simple example.
Let's say you have an array of customers.

                                        
    var customers = Array("Mike", "Zara","Abdul","Peter")                 
                                    

Now you want to send a greeting message to all of these clients. The most obvious method to do it is a loop. It may be something like this.

                                        
    for ( i <- 0 to customers.length-1) {
        println("Hi " + customers(i));
        }                 
                                    

It's a quite simple code. For each customer, we want to call a function println. Instead of println, you may have some other function. For example payment reminder.

                                        
    def remindPayment(x: String) = println("Payment reminder for " + x)                    
                                    

The above code is a dummy function for payment reminder. The new loop looks as below.

                                        
    var i=0;
    for ( i <- 0 to customers.length-1) {
        remindPayment(customers(i));
    }                    
                                    

So, what are we doing? In fact, we want to say this.
For each customer, remind payment
If you design the above scenario using a higher order function. Your final code should look like this.

                                        
    forEach(customers, remindPayment)                    
                                    

This line does the same thing. However, I have abstracted the implementation details behind a higher order function. The forEach is a higher order function, and it takes two parameters. This type of programming approach is more precise, easy to understand and more reusable. I can reuse the same function to send payment to all my vendors.

                                        
    forEach(vendors, sendPayment)                    
                                    

The below code shows a simple method to implement the forEach function.

                                        
    def forEach(a: Array[String], f:String => Unit) = {
        for ( i <- 0 to a.length-1) 
                f(a(i));
        }                    
                                    

The forEach function takes two parameters. The first parameter is an array of strings. The second parameter is a function. We loop through the array and call the function. In fact, Scala provides this kind of iterator method for every collection. So you do not need to implement a forEach function.


                                        
    customers foreach remindPayment                    
                                    

The above line is using Scala's built in foreach.
It says - take customers, and for each customer, remind payment.
Keep reading for more interesting functional concepts.

Read More

Pure Functions | Referential Transparency | Benefits of pure functions | First class functions | Higher order function | Anonymous functions | Immutability | Tail Recursion | Expressions in Scala | Lazy Evaluations | Pattern Matching | Closures

By Prashant Pandey -


You will also like:


Kafka Core Concepts

Learn Apache Kafka core concepts and build a solid foundation on Apache Kafka.

Learning Journal

Scala placeholder syntax

What is a scala placeholder syntax and why do we need it? Learn from experts.

Learning Journal

Pattern Matching

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

Learning Journal

Pure Function benefits

Pure Functions are used heavily in functional programming. Learn Why?

Learning Journal

Immutability in FP

The literal meaning of Immutability is unable to change? How to program?

Learning Journal