This is an excerpt from the 1st Edition of the Scala Cookbook (#ad) (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 |