Hi, community in this blog we are gonna talk about one very important feature of Functional style programming in Scala so make your IntelliJ ready and let’s learn about Higher-Order Functions.
Before going into Higher Order Function first let’s take a brief understanding of Functions in Scala.
Functions in Scala
As we all know, a function is a group of statements that performs a particular task.
How will you know that you have to use a function?
When a particular code is repeating itself again and again in your program then you have to consider enclosing that piece of code into a function.
Let’s create a simple program for performing addition between two numbers:
object function {
def add(x: Int, y: Int) : Int = {
x + y
}
def main(args: Array[String]): Unit = {
println(add(210, 100))
}
}
Output: 310
In Scala, functions are treated as first-class citizens which means that you can assign a function to a variable and that is done using Anonymous Function (lambda). For example:
object function {
var add = (x: Int, y: Int) => x + y
def main(args: Array[String]): Unit = {
println(add(210, 100))
}
}
The above program will also give the same Output: 310
Now as we have understood the concept of Functions in Scala let’s move into the Higher-Order Function
What are Higher Order Function
Those functions can take a function as an argument and also can give a function as result.
Let’s take an example in which function is taking a function as an argument
object demoHigherOrder {
val double = (i: Int) => i * 2 // Anonymous Function
def demo(x: Int, y: Int => Int) = y(x) // Higher Order Function
def main(args: Array[String]): Unit = {
println(demo(10, double))
}
}
Output: 20
Explanation: The above program is an example of a higher-order function where we are taking a function as an argument. We have declared an anonymous function double which is taking an “int” and returning the double of that “int” and then we are passing any function named y(which also takes an Int and return Int) into our function demoHigherOrder as an argument along with x(which also takes an Int)
Then we are passing y over x as (y(x)) and in the main function, we are calling the higher-order demo with value 10 and the function double.
Function that Returns another Function
object Function extends App {
def sayHello = (name: String) => {"Hello" + " " + name}
var message = sayHello("Knoldus")
println(message)
}
Output: Hello Knoldus
Explanation: In the above program we have a program function named sayHello and it’s equal to another function, therefore, we can say that it returns that function when we call sayHello but instead of returning the function it’s then invoked at the same time and it substitutes name with “Knoldus” and it will result in Hello Knoldus.
As we all know Scala allows Type inference therefore the message will also return a String since the function sayHello is returning a String.
Why to use Higher Order Function?
Higher-Order Function enables you to create control abstraction that allows you to reduce code duplication. Let’s take a real-life coding example to get the more clear picture.
Suppose you want to create an API that allows users to search for files matching some criterion like first you want to search for files whose names end in a particular string and your API looks like below:
object FileMatcher {
private def filesHere = (new java.io.File(".")).listFiles
def filesEnding(query: String) =
for (file <- filesHere; if file.getName.endsWith(query))
yield file
}
So far so good but now your user is asking for the feature that lets people search based on any part of the file name. So you add this feature to your API and your code looks like this:
def filesContaining(query: String) =
for (file <- filesHere; if file.getName.contains(query))
yield file
It’s been some time and your API is working absolutely fine but now you want to search based on regular expressions.
Higher-Order Approach
Now here an experienced programmer will notice all of this repetition and wonder if it can be factored into a common helper function and will follow a higher-order approach and try to demonstrate the way in which first-class functions can help you eliminate code duplication where it would be very difficult to do so without them as shown below:
object FileMatcher {
private def filesHere = (new java.io.File(".")).listFiles
private def filesMatching(matcher: String => Boolean) =
for (file <- filesHere; if matcher(file.getName))
yield file
def filesEnding(query: String) =
filesMatching(_.endsWith(query))
def filesContaining(query: String) =
filesMatching(_.contains(query))
def filesRegex(query: String) =
filesMatching(_.matches(query))
}
In the above program, the function literal _.endsWith(_), used in the filesEnding method, means the same thing as:
(fileName: String, query: String) => fileName.endsWith(query)
Conclusion
Thank you guys for making it to the end of this blog.
Via reading till now you must have the idea of how Scala’s rich function support building control abstractions. You can use functions within your code to factor out common control patterns, and you can take advantage of higher-order functions in the Scala library to reuse control patterns that are common across all programmers’ code.
Refrences
https://www.artima.com/pins1ed/control-abstraction.html
https://docs.scala-lang.org/tour/higher-order-functions.html
I went over multiple sites for this info, landed on what I’ve been looking for, quite good.