First Class Function and Higher Order Function
One of the key factors in Scala that beautifully blends functional paradigm into object-oriented paradigm is that functions are objects.
In functional programming, functions are first-class citizens.
A first-class function is a function that can be
– Assigned to variables,
– Passed as an argument to the other function, and
– Returned as values from the other function.
The other function emphasized in point 2 that takes a function as an argument and the other function emphasized in point 3 that returns a function, are called higher-order functions. In the sections that follow, you will learn about all three aspects of a first class function.
Function as Variable
Just as you pass String, Int, and other variables around in an OOP, you can pass a function around like a variable. You can define a function literal, and then assign that literal to a variable. The following code defines a function literal that takes an Int parameter and returns a value that is twice the amount of the Int that is passed in:
(i: Int) => { i * 2 }
You can now assign that function literal to a variable:
val doubler = (i: Int) => { i * 2 }
scala> val doubler = (i: Int) => { i * 2 }
doubler: Int => Int =
The variable doubler is an instance of a function, known as a function value. You can now invoke doubler as illustrated here:
doubler(2)
scala> doubler(2)
res25: Int = 4
Under the hood, doubler is an instance of the Function1 trait, which defines a function that takes one argument. In terms of implementation, doubler is a function created using the keyword val and assigned to a variable. To define the doubler as a method instead of as a function you have to define the doubler method in a class, and use the keyword def to define a method.
Beyond just invoking doubler, you can also pass it to any function (or method) that takes a function parameter.
Function as Parameter
You can create a function or a method that takes a function as a parameter. For this, first define a method that takes a function as a parameter.
def operation(functionparam:(Int, Int) => Int) {
println(functionparam(4,4))
}
scala> def operation(functionparam:(Int, Int) => Int) {
| println(functionparam(4,4))
| }
operation: (functionparam: (Int, Int) => Int)Unit
The operation method takes one parameter named functionparam, which is a function. The functionparam function takes two Int and returns an Int. The operation method returns a Unit that indicates that operation method returns nothing.
Next, define a function that matches the expected signature. The following add function matches that signature, because it takes two Int arguments and returns an Int:
val add = (x: Int, y:Int) => { x + y }
Now you can pass an add function into the operation method:
operation(add)
scala> operation(add)
8
Any function that matches this signature can be passed into the operation method. Let’s define two new functions named subtract and multiply that take two Int and
return an Int:
val subtract = (x: Int, y:Int) => { x – y }
val multiply = (x: Int, y:Int) => { x*y }
Now you can pass these functions into your operation method:
operation(subtract)
scala> operation(subtract)
0
operation(multiply)
scala> operation(multiply)
16
Returning a Function
You can return a function from a function or method. In order to do this, first define an anonymous function.
The following code declares an anonymous function that takes a String argument and returns a String:
(name: String) => { “hello” + ” ” + name }
Now we will define a method that returns the anonymous function that we just defined.
def greeting() = (name: String) => {“hello” + ” ” + name}
On the left side of the = symbol you have a normal method declaration:
def greeting()
On the right side of the = is a function literal (an anonymous function):
(name: String) => {“hello” + ” ” + name}
scala> def greeting() = (name: String) => {“hello” + ” ” + name}
greeting: ()String => String
Now you can assign greeting() to a variable:
val greet= greeting()
scala> val greet= greeting()
greet: String => String =
The greet function is now equivalent to your anonymous function (name: String) => {“hello” + ” ” + name}.
Because the anonymous function takes a String parameter name, you can pass it a name:
greet(“Reader”)
scala> greet(“Reader”)
res26: String = hello Reader
Closure
A closure is a function, whose return value depends on the value of one or more variables declared outside this function. Consider the following multiplier function:
val multiplier = (x:Int) => x * 3
In the multiplier function, i is the variable used in the function body. x is defined as a parameter to the function. Now let’s modify the multiplier function as illustrated here:
val multiplier = (x:Int) => x * y
Because is a formal parameter to the function, it is bound to a new value each time multiplier is called. However, j is not a formal parameter. Let’s further modify the multiplier function as illustrated:
var y = 3
val multiplier = (x:Int) => x * y
Now, y has a reference to a variable outside the function but in the enclosing scope.
scala> var y = 3
y: Int = 3
scala> val multiplier = (x:Int) => x * y
multiplier: Int => Int =
Now you can invoke the multiplier function as illustrated here:
multiplier(3)
scala> multiplier(3)
res37: Int = 9
The multiplier function references j and reads its current value each time. The Scala compiler creates a closure (closes over) that encompasses the variable in the enclosing scope.
Partially Applied Function
In functional programming languages, when you call a function that has parameters, you are said to be applying the function to the parameters. When all the parameters are passed to the function you have fully applied the function to all the parameters.
A simple add function:
val add = (x: Int, y: Int) => x + y
scala> val add = (x: Int, y: Int) => x + y
add: (Int, Int) => Int =
scala> add(1,2)
res01: Int = 3
But when you give only a subset of the parameters to the function, the result of the expression is a partially applied function.
val partiallyAdd = add(1, _:Int)
Because you haven’t provided a value for the second parameter, the variable partiallyAdd is a partially applied function. You can see this in the REPL:
scala> val partiallyAdd = add(1, _:Int)
partiallyAdd: Int => Int =
The output in the REPL shows that partiallyAdd is a function that implements the Function1 trait. Implemeting a Function1 trait indicates that partiallyAdd function takes one argument. When you give partiallyAdd an Int value 2, you get the sum of the Int number passed into the add and partiallyAdd functions:
scala> partiallyAdd(2)
res02: Int = 3
The first argument 1 was passed into the original add function and the new function named partiallyAdd was created, which is a partially applied function; then, the second argument 2 was passed into partiallyAdd. When you provide all the parameters, the original function is executed, yielding the result.
References:
Beginning Scala, Second Edition
By: David Pollak; Vishal Layka