In this blog post, we will learn about higher-order functions in Scala – what they mean, why they are used and how they are used.
What is a higher-order function?
A higher-order function is a function that takes in another function as argument and itself returns some value or function. Some examples of higher-order functions include map, filter, reduce, foreach etc.
Why are they used?
As a part of the functional programming paradigm, whatever logic we need to write is to be implemented in terms of pure and immutable functions. Here, functions take arguments from other functions as input and return values/functions which used by other functions for further processing. Here, pure means that the function does not produce any side-effects like printing to the console and immutable means that the function takes in and produces immutable data(val) only.
Higher-order functions comply with the above idea. As compared to for loops, we can iterate a data structure using higher-order functions with much less code.
How do we use higher-order functions?
Let’s move forward and try using some higher order functions. We will be seeing 3 most commonly used higher-order functions which are map, filter and reduce.
A map higher-order function applies the function passed in it to every element of the data structure. It then returns the same type of data structure but with mapped values. It has the following form :
def map[B] (f: A => B): Traversable[B]
If you have sbt installed, just type “sbt console” in any directory to get to the Scala REPL and you can try all the examples given here. If not, you should install sbt first.
EXAMPLE : Given a vector of integers from 1 to 10, we will try to find the corresponding vector that contains the squares of those integers. We perform this using map function as below :
Here, the map function is used over a vector of integers in Scala. The function passed into the map function i => i*i takes each integer from the numbers vector and converts it to the square of that integer. This happens for all the integers and the resulting vector containing squares of numbers from 1 to 10 is returned. Doing the same in Java would take many more lines.
A filter function takes in a predicate and selects the elements from the data structure which satisfy the given predicate. A predicate is a unary function that returns a boolean value. Here again we get the same type of data structure as the input. A filter function looks like:
def filter(f: A => Boolean): Traversable[A]
EXAMPLE : Given a vector of integers from 1 to 10, we will try to pull out all even numbers integers in a new vector. Have a look at the code below :
Here, the filter function is taking in a predicate x => x%2==0 which is a mapping from an integer to a boolean value based on the condition that the integer is even. If the integer is even, the predicate returns true and the integer is added to the resulting data structure. If the integer is odd, the predicate returns false and it is discarded. The result is a vector of all even integers.
A reduce function reduces the elements of the data structure using the specified associative binary operator and returns the reduced value. Reduction means to apply the same binary operator consecutively on each element to produce a single value. It is of the form :
def reduce[B >: A](op: (B, B) => B): B
EXAMPLE : Let’s try to sum the numbers present in a vector using reduce function.
Here in the reduce function, we pass in a binary associative operator (x , y) => x+y which at a time takes two integers from the data structure and adds them. This operation is both binary i.e. operating on 2 elements x & y and associative as x+y=y+x. The added result is then again summed up with another integer from the vector. This process continues till all the integers are summed up and the total sum is returned.
In this blog we learnt about higher-order functions and how we can use them. Keep learning!