Scala: Higher-order and anonymous functions
Now after we have considered basics of Scala functions, we can make a next step in more complex use cases functional programming. Since functions in Scala are objects we can pass them as arguments in another functions, also we can return them as a result. In this article I’m going to examine multiple examples of higher-order functions.
What functions? A function which accept another functions as arguments or returns a function is a higher-order function. It sounds like a madness if you have a previous experience with object oriented programming, but after the reading you’d definitely understand how it works.
Function as parameter
At first let’s do what we already know how to do – declare a simple function.
def halfMaker(value: Int): Double = value.toDouble/2
This is a small function with a trivial logic: accept Int
value and return a half of it in a Double
type.
What about one more simple function?
def addFive(value: Int): Int = value+5
addFive
function even more trivial than the halfMaker
. It accepts Int
value and adds to it 5.
What is common in these two functions? They both accept Int
parameter and return an AnyVal
value. So in general we can describe these functions by following signature Int => AnyVal
.
Now let’s declare a function which has a function argument among other parameters.
def processRange(start: Int, finish: Int, processor: Int => AnyVal): Unit = { for (i <- start to finish) println(processor(i)) }
As you see processRange
for each Int
value between the start
and finish
, applies the processor
function and prints a result. The type of the processor parameter is Int => AnyVal
. It matches the signatures of two first functions which we declared earlier in the article.
Here is a demonstration of this code in action:
Of course you need to declare all of functions before their usage. A function passed as argument to another function is a callback function.
Let’s continue with one more example of callback functions in Scala.
def moneyTransfer(amount: Double, providerFee: Double => Double): Double = { amount + 10 + providerFee(amount) } def providerA(money: Double) = money * 0.05 println(moneyTransfer(100, providerA)) //The output is 115.0
This code isn’t good from the functional point of view, it doesn’t check that the amount
is positive and more than 10 etc, but this example is just for demonstration of a callback function.
So let’s assume that we have an application which works with money transfers. We are a service provider. If people want to send money, they come to us and we give them an interface for this. In our turn, we use already existing services for this, e.g. Provider A, Provider B etc. Our fee is $ 10. Depending on provider, an extra charge occurs and they vary from provider to provider.
The moneyTransfer
function accepts two arguments: amount
– amount of money which customer wants to transfer and providerFee
is a callback function with signature Double => Double
. The purpose of moneyTransfer
function is to calculate total which customer needs to provide in order to make a transfer.
By passing a function as argument you inject an extra logic inside of the higher-order function. So it’s very powerful mechanism to make you code more flexible and efficient. In the same time you can complicate a simple code by redundant callbacks usage.
Anonymous function
Some times you don’t need to declare a function before its usage because it is need to be used just in one place. For this case anonymous functions suit well. Let’s use one of previous example for demonstration:
def moneyTransfer(amount: Double, providerFee: Double => Double): Double = { amount + 10 + providerFee(amount) } println(moneyTransfer(100, moneyTransfer(250, m => m / 10))) //The output is 285.0
So instead of declaring a regular function with signature Double => Double
, we directly passed the anonymous function m => m / 10
. The type of m
is omitted because it can be defined based on context where the anonymous function is invoked.
//Also valid moneyTransfer(100, moneyTransfer(250, (m: Double) => m / 10))
Sometimes an anonymous function is called lambda function. So you can use this name as alias for anonymous function.
Function as return value
The most rare case of higher-order functions is when a function returns another function. When it’s handy? If to been honest, I still don’t know good use cases for this. But Scala gives you chance to do this.
So let’s consider the following example:
def getStrategy(enoughEnergy: Boolean) = { if (enoughEnergy) (energy: Double) => "We are going to attack with damage "+energy else (energy: Double) => "We are going to reflect damage "+energy/2 } val returnedFunction = getStrategy(true) returnedFunction(15.0) //We are going to attack with damage 15.0
That’s how a function can be returned as a result.
Summary
Now you know how to use higher-order functions. Maybe it’s hard to start use them almost instantly after the reading of this article, but I believe that you will be able to read and understand a code where higher-order functions are used. The easiest part of the blog post is about anonymous functions. They are compact and behave like normal functions, except that circumstance that they don’t have names.