Higher Order Function
Remarks#
Scala goes to great lengths to treat methods and functions as syntactically identical. But under the hood, they are distinct concepts.
A method is executable code, and has no value representation.
A function is an actual object instance of type Function1
(or a similar type of another arity). Its code is contained in its apply
method. Effectively, it simply acts as a value that can be passed around.
Incidentally, the ability to treat functions as values is exactly what is meant by a language having support for higher-order functions. Function instances are Scala’s approach to implementing this feature.
An actual higher-order function is a function that either takes a function value as an argument or returns a function value. But in Scala, as all operations are methods, it’s more general to think of methods that receive or return function parameters. So map
, as defined on Seq
might be thought of as a “higher-order function” due to its parameter being a function, but it is not literally a function; it is a method.
Using methods as function values
The Scala compiler will automatically convert methods into function values for the purpose of passing them into higher-order functions.
object MyObject {
def mapMethod(input: Int): String = {
int.toString
}
}
Seq(1, 2, 3).map(MyObject.mapMethod) // Seq("1", "2", "3")
In the example above, MyObject.mapMethod
is not a function call, but instead is passed to map
as a value. Indeed, map
requires a function value passed to it, as can be seen in it’s signature. The signature for the map
of a List[A]
(a list of objects of type A
) is:
def map[B](f: (A) ⇒ B): List[B]
The f: (A) => B
part indicates that the parameter to this method call is some function that takes an object of type A
and returns an object of type B
. A
and B
are arbitrarily defined. Returning to the first example, we can see that mapMethod
takes an Int
(which corresponds to A
) and returns a String
(which corresponds to B
). Thus mapMethod
is a valid function value to pass to map
. We could rewrite the same code like this:
Seq(1, 2, 3).map(x:Int => int.toString)
This inlines the function value, which may add clarity for simple functions.
High Order Functions(Function as Parameter)
A higher-order function, as opposed to a first-order function, can have one of three forms:
-
One or more of its parameters is a function, and it returns some value.
-
It returns a function, but none of its parameters is a function.
-
Both of the above: One or more of its parameters is a function, and it returns a function.
object HOF { def main(args: Array[String]) { val list = List(("Srini","E"),("Subash","R"),("Ranjith","RK"),("Vicky","s"),("Sudhar","s")) //HOF val fullNameList= list.map(n => getFullName(n._1, n._2)) } def getFullName(firstName: String, lastName: String): String = firstName + "." + lastName }
Here the map function takes a getFullName(n._1,n._2)
function as a parameter. This is called HOF (Higher order function).
Arguments lazy evaluation
Scala supports lazy evaluation for function arguments using notation: def func(arg: => String)
. Such function argument might take regular String
object or a higher order function with String
return type. In second case, function argument would be evaluated on value access.
Please see the example:
def calculateData: String = {
print("Calculating expensive data! ")
"some expensive data"
}
def dumbMediator(preconditions: Boolean, data: String): Option[String] = {
print("Applying mediator")
preconditions match {
case true => Some(data)
case false => None
}
}
def smartMediator(preconditions: Boolean, data: => String): Option[String] = {
print("Applying mediator")
preconditions match {
case true => Some(data)
case false => None
}
}
smartMediator(preconditions = false, calculateData)
dumbMediator(preconditions = false, calculateData)
smartMediator
call would return None value and print message "Applying mediator"
.
dumbMediator
call would return None value and print message "Calculating expensive data! Applying mediator"
.
Lazy evaluation might be extremely useful when you want to optimize an overhead of expensive arguments calculation.