Scala Foundation Course - Higher Order Functions


In this session, we will learn about the syntax for creating Higher Order function in Scala. I am assuming that you already learned the definition of a Higher Order function. This lesson will primarily focus on the syntactical part.

How to return a function in Scala?

We learned a lot about functions. I hope you can create a function. So, let me ask you a question.
Can you create a function that takes an integer x as an input? And then it does following things.

  1. Print the SQRT of the input parameter x.
  2. Returns a function that takes another integer y as input.

The returned function returns the SQRT of x + y.
Let me show you the result, so you have a better understanding of the requirement.

                                
    val f2 = f1(5)
    /*Output
    SQRT of 5 is 2.23606797749979
    f2: Int => Double = <function1>
    */                                          
                         

So, f1 is a function that takes an integer. It prints the SQRT of the value 5. It also returns a new function f2.

                                
    f2(20)
    //res0: Double = 5.0                                        
                         

The f2 takes another integer 20. It adds 5 and 20 that becomes 25. Then it returns the SQRT of 25.
The function f1 is just a useless dummy example. But this silly example can help you evaluate yourself that you can create a Higher Order function in Scala. Here is the solution.

                                
    def f1(x:Int) = {
            println(s"SQRT of $x is " + Math.sqrt(x))
            (y:Int) => Math.sqrt(x+y)
        }
    val f1 = (x:Int) => {
        println(s"SQRT of $x is " + Math.sqrt(x))
        (y:Int) => Math.sqrt(x+y)
    }                                       
                         

The first part is using a standard def syntax. The second part is using a function literal. Do you understand what's happening here? Both the examples are same and using the same technique to return a function. Let's pick the first one and try explaining it.
Look at the body of the function. We do some processing there and finally create a local anonymous function as the last expression. Scala will automatically return the last expression. Right? You already learned that. So, in this case, Scala will Return this local function. That's it. That's the trick.
The Same thing is happening in both the above examples. So, if you want to return a function from a Higher Order function, all you have to do is to create an anonymous local function as the last expression of the outer function.
The most important point to remember is that the function that you want to return, that must be an anonymous function.
If you give it a name, it becomes an ordinary local function.

                                
    def f1(x:Int) = {
        println(s"SQRT of $x is " + Math.sqrt(x))
            def f2(y:Int) = Math.sqrt(x+y)
        }
        /* Output - It return a Unit
        f1: (x: Int)Unit
        */
        //You can still return an ordinary local function, but you have to do some extra work. 
        def f1(x:Int) = {
            println(s"SQRT of $x is " + Math.sqrt(x))
            def f2(y:Int) = Math.sqrt(x+y)
            f2 _
        }                                  
                         

Now I am forcefully returning f2. The underscore in the above example is the part of the partially applied functions concept. I will cover that in a separate video.

Function currying syntax

Great. I guess you learned the method of returning a function from a Higher Order Function. It's straightforward. Simply create an anonymous function in the end, and Scala will return it for you.
You can execute f1 like this.

                                
    val f2 = f1(3)                                          
                         

It returns a function that takes another parameter. You can hold the return value as f2 and make the next call. That's a two-step process. Right? You can achieve the same thing in a single step.
Like this.

                                
    f1(3)(5)                                           
                         

This single step process, I mean, calling functions in a series is popularly known as function currying. We will come back to this topic in a separate video.

Shorthand syntax for returning a function

Now, let me confuse you. Or rather say, eliminate the source of confusion. Look at the function shown below.

                                
    def f1(x:Int) = {
        println(s"SQRT of $x is " + Math.sqrt(x))
        (y:Int) => Math.sqrt(x+y)
    }                                       
                         

Suppose I don't need that println. All I want is to return a function. That's it. If I remove the println, the code looks like this.

                                
    def f1(x:Int) = {
        (y:Int) => Math.sqrt(x+y)
    }                                       
                         

My local function is a single expression. Right? So, I don't need the curly braces. The code looks like this.

                                
    def f1(x:Int) = (y:Int) => Math.sqrt(x+y)                                           
                         

Similarly, after applying all these changes for the literal syntax, the function should look like this.

                                
    val f1 = (x:Int) => (y:Int) => Math.sqrt(x+y)                                           
                         

These simplified formats are quite complex to understand. I often see people not able to follow the syntax for Higher Order functions.
Sometimes, people make it more complicated by specifying the type annotation. Look at this.

                                
    val f1: Int => (Int => Double) = (x:Int) => (y:Int) => Math.sqrt(x+y)                                           
                         

I mean, this code spins my head among so many right arrows.
But actually, the part : Int => (Int => Double) is redundant because Scala will infer the return type automatically. We don't need to keep it there.
The Higher Order function syntax is nothing but returning a local function from the Higher Order function body. If you are looking at some code and getting confused, just remove return types, add curly braces and things will start making sense.
Here is an example of a function literal.

                                
    val fs:String => (String => String) = (prefix: String) => (s: String) => prefix + " " + s          
                         

Looks complicated? Pause for a minute and try to explain? What this function is doing?
If you are getting lost among the right arrows, remove the return type. I mean, start with the colon and go up to the first equal to symbol. Delete that part, and it becomes little simple.

                                
    val fs = (prefix: String) => (s: String) => prefix + " " + s                                           
                         

Now, add a curly brace after the first right arrow.

                                
    val fs = (prefix: String) => { (s: String) => prefix + " " + s }                                           
                         

Now, you know that the part left to the => is the list of parameters. And the part right to the => is the body. And within the body, we have a function literal that returns prefix plus s.
So, the fs will take one parameter named prefix. It will return another function. The new function takes a string s. It concatenates the prefix and the s. That's it.

                                
    fs("Hi")("There")                                           
                         

One last thing on a function returning a function.
If the local anonymous function doesn't take a parameter, the code simplifies to this.

                                
    val fs = (prefix: String) =>{ () => prefix + " " + "There" }                                           
                         

Empty parenthesis for the local function and you can call it like this.

                                                                    
    fs("Hi")()                                      
                         

Good. I hope you won't get confused over the syntax for returning a function.

How to pass a function to a Higher Order function in Scala?

The other side of Higher Order function is passing a function as a parameter. That part is quite straightforward.
Let's say I want to create an integer decorator function. It takes an integer and also takes a function. The function f converts an integer to a decorated string. Right?

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

So, I define the function parameter like f: Int => String.
It takes an Integer and gives a String. The body of my Higher Order function is simple. Apply f on x. That's it.
Now, we can call this function. Pass an integer. Then pass a function literal with the logic to decorate it.

                                
    intDecorator(5, (y:Int) => "[" + y + "]" )                                           
                         

You want a different decoration, pass a different logic.

                                
    intDecorator(5, (y:Int) => "" + y + "" )                                          
                         

Applying placeholder syntax to Higher Order functions

Now, It's time for a quiz.
Remember? You learned about placeholder syntax. It allows you to eliminate the list of parameters. Can you apply the placeholder syntax in the above example? Pause the video and try it.
Here is how you can do it.

                                
    intDecorator(5, "[" + _.toString + "]" )                                         
                         

Remove the first part. Right? Use an underscore instead of using y. That's it.
Great. That's all about Higher Order functions.
Thank you very much for watching Learning Journal. Keep Learning and Keep Growing.


You will also like:


Kafka Core Concepts

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

Learning Journal

Hadoop Security

Hadoop security implementation using Kerberos.

Learning Journal

Functional Programming

What is Functional Programming and why it is important?

Learning Journal

Lazy Evaluations

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

Learning Journal

Scala Variable length arguments

How do you create a variable length argument in Scala? Why would you need it?

Learning Journal