This is an excerpt from the 1st Edition of the Scala Cookbook (partially modified for the internet). This is Recipe 9.2, “How to use functions as variables (values) in Scala.”
Problem
You want to pass a Scala function around like a variable, just like you pass String
, Int
, and other variables around in an object-oriented programming language.
Solution
Use the syntax shown in Recipe 9.1 to define a function literal, and then assign that literal to a variable.
The following Scala 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 }
As mentioned in Recipe 9.1, you can think of the =>
symbol as a transformer. In this case, the function transforms the Int
value i
to an Int
value that is twice the value of i
.
You can now assign that function literal to a variable:
val double = (i: Int) => { i * 2 }
The variable double
is an instance, just like an instance of a String
, Int
, or other type, but in this case, it’s an instance of a function, known as a function value. You can now invoke double
just like you’d call a method:
double(2) // 4 double(3) // 6
Beyond just invoking double
like this, you can also pass it to any method (or function) that takes a function parameter with its signature. For instance, because the map
method of a sequence is a generic method that takes an input parameter of type A
and returns a type B
, you can pass the double
method into the map
method of an Int
sequence:
scala> val list = List.range(1, 5) list: List[Int] = List(1, 2, 3, 4) scala> list.map(double) res0: List[Int] = List(2, 4, 6, 8)
Welcome to the world of functional programming.
Discussion
You can declare a function literal in at least two different ways. I generally prefer the following approach, which implicitly infers that the following function’s return type is Boolean
:
val f = (i: Int) => { i % 2 == 0 }
In this case, the Scala compiler is smart enough to look at the body of the function and determine that it returns a Boolean
value. As a human, it’s also easy to look at the code on the right side of the expression and see that it returns a Boolean
, so I usually leave the explicit Boolean
return type off the function declaration.
However, if you prefer to explicitly declare the return type of a function literal, or want to do so because your function is more complex, the following examples show different forms you can use to explicitly declare that your function returns a Boolean
:
val f: (Int) => Boolean = i => { i % 2 == 0 } val f: Int => Boolean = i => { i % 2 == 0 } val f: Int => Boolean = i => i % 2 == 0 val f: Int => Boolean = _ % 2 == 0
A second example helps demonstrate the difference of these approaches. These functions all take two Int
parameters and return a single Int
value, which is the sum of the two input values:
// implicit approach val add = (x: Int, y: Int) => { x + y } val add = (x: Int, y: Int) => x + y // explicit approach val add: (Int, Int) => Int = (x,y) => { x + y } val add: (Int, Int) => Int = (x,y) => x + y
As shown, the curly braces around the body of the function in these simple examples are optional, but they are required when the function body grows to more than one expression:
val addThenDouble: (Int, Int) => Int = (x,y) => { val a = x + y 2 * a }
Using a method like an anonymous function
Scala is very flexible, and just like you can define an anonymous function and assign it to a variable, you can also define a method and then pass it around like an instance variable. Again using a modulus example, you can define a method in any of these ways:
def modMethod(i: Int) = i % 2 == 0 def modMethod(i: Int) = { i % 2 == 0 } def modMethod(i: Int): Boolean = i % 2 == 0 def modMethod(i: Int): Boolean = { i % 2 == 0 }
Any of these methods can be passed into collection methods that expect a function that has one Int
parameter and returns a Boolean
, such as the filter
method of a List[Int]
:
val list = List.range(1, 10) list.filter(modMethod)
Here’s what that looks like in the REPL:
scala> def modMethod(i: Int) = i % 2 == 0 modMethod: (i: Int)Boolean scala> val list = List.range(1, 10) list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9) scala> list.filter(modMethod) res0: List[Int] = List(2, 4, 6, 8)
As noted, this is similar to the process of defining a function literal and assigning it to a variable. The following function works just like the previous method:
val modFunction = (i: Int) => i % 2 == 0 list.filter(modFunction)
At a coding level, the obvious difference is that modMethod
is a method defined in a class, whereas modFunction
is a function that’s assigned to a variable. Under the covers, modFunction
is an instance of the Function1
trait, which defines a function that takes one argument. (The scala package defines other similar traits, including Function0
, Function2
, and so on, up to Function22
.)
Assigning an existing function/method to a function variable
Continuing our exploration, you can assign an existing method or function to a function variable. For instance, you can create a new function named c
from the scala.math.cos method using either of these approaches:
scala> val c = scala.math.cos _ c: Double => Double = <function1> scala> val c = scala.math.cos(_) c: Double => Double = <function1>
This is called a partially applied function. It’s partially applied because the cos
method requires one argument, which you have not yet supplied (more on this in Recipe 9.6).
Now that you have c
, you can use it just like you would have used cos
:
scala> c(0) res0: Double = 1.0
If you’re not familiar with this syntax, this is a place where the REPL can be invaluable. If you attempt to assign the cos
function/method to a variable, the REPL tells you what’s wrong:
scala> val c = scala.math.cos <console>:11: error: missing arguments for method cos in class MathCommon; follow this method with `_' to treat it as a partially applied function val c = scala.math.cos ^
The following example shows how to use this same technique on the scala.math.pow method, which takes two parameters:
scala> val p = scala.math.pow(_, _) pow: (Double, Double) => Double = <function2> scala> p(scala.math.E, 2) res0: Double = 7.3890560989306495
If this seems like an interesting language feature, but you’re wondering where it would be useful, see Recipe 9.6, “Using Partially Applied Functions”, for more information.
Summary notes
- Think of the
=>
symbol as a transformer. It transforms the input data on its left side to some new output data, using the algorithm on its right side. - Use
def
to define a method,val
to create a function. - When assigning a function to a variable, a function literal is the code on the right side of the expression.
- A function value is an object, and extends the
FunctionN
traits in the main scala package, such asFunction0
for a function that takes no parameters.
See Also
- The Function1 trait
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |