O'Reilly logo

Learning Scala by Jason Swartz

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 5. First-Class Functions

One of the core values of functional programming is that functions should be first-class. The term indicates that they are not only declared and invoked but can be used in every segment of the language as just another data type. A first-class function may, as with other data types, be created in literal form without ever having been assigned an identifier; be stored in a container such as a value, variable, or data structure; and be used as a parameter to another function or used as the return value from another function.

Functions that accept other functions as parameters and/or use functions as return values are known as higher-order functions. You may have heard of two of the most famous higher-order functions, map() and reduce(). The map() higher-order function takes a function parameter and uses it to convert one or more items to a new value and/or type. The reduce() higher-order function takes a function parameter and uses it to reduce a collection of multiple items down to a single item. The popular Map/Reduce computing paradigm uses this concept to tackle large computing challenges, by mapping the computation across a range of distributed nodes and reducing their results back to a meaningful size.

One of the benefits of using higher-order functions to work with data is that the actual how of processing the data is left as an implementation detail to the framework that has the higher-order function. A caller can specify what should be done and leave the higher-order functions to handle the actual logic flow. There’s actually a name for this methodology, declarative programming, typically correlated with the use of functional programming and indicating that higher-order functions or some other mechanism is used to simply declare the work to be done without manually doing it. The opposite of this approach is the more mundane imperative programming style, wherein the logic flow of an operation is always explicitly stated.

So how does all of this apply to Scala?

Scala has full support for first-class functions, higher-order functions, and the use of declarative programming. As with other types of data such as String or Int, functions have types based on the types of their input arguments and their return value. A function can be stored in a value or variable, passed to a function, returned from a function, and used with data structures to support map(), reduce(), fold(), and filter(), among many other higher-order functions.

In this chapter, we will explore Scala’s use of first-class functions, higher-order functions, and the use of function literals to easily create or pass logical expressions anywhere a regular function could be used.

Function Types and Values

The type of a function is a simple grouping of its input types and return value type, arranged with an arrow indicating the direction from input types to output type.

Syntax: A Function Type

([<type>, ...]) => <type>

Until now, all of the types we have used have been simple words like String and Int, so a type that includes punctuation and whitespace is likely to appear to be a bit odd. However, if you think about it, this is the only way to really describe a function without using a specific name. Because a function’s signature is its name, inputs, and outputs, the type of a function should be the inputs and outputs.

For example, the function def double(x: Int): Int = x * 2 has the function type Int => Int, indicating that it has a single Int parameter and returns an Int. The function name, “double,” is an identifier and isn’t part of the type. The body of the function, a simple multiplication of the input by 2, does not affect the type of the function. The rest of the information is the input types and return type, and so these make up the function type itself.

Let’s try using a function type in the REPL, by creating a function and then assigning it to a function value:

scala> def double(x: Int): Int = x * 2
double: (x: Int)Int

scala> double(5)
res0: Int = 10

scala> val myDouble: (Int) => Int = double                                1
myDouble: Int => Int = <function1>

scala> myDouble(5)                                                        2
res1: Int = 10

scala> val myDoubleCopy = myDouble
myDoubleCopy: Int => Int = <function1>

scala> myDoubleCopy(5)                                                    3
res2: Int = 10
1

myDouble is just a value, except that unlike other values it can be invoked.

2

Invoking myDouble as a function has the same result as invoking double.

3

Assigning a function value to a new value works as with any other value.

The explicit type for the “myDouble” value was required to distinguish it as a function value and not a function invocation. An alternate way to define function values, assigned with a function, is with the wildcard operator, _.

Note

Function types with a single parameter can leave off the parentheses. For example, a function that takes and returns a single integer can be written as the type Int => Int.

Syntax: Assigning a Function with the Wildcard Operator

val <identifier> = <function name> _

Let’s try this out with the “myDouble” function value:

scala> def double(x: Int): Int = x * 2
double: (x: Int)Int

scala> val myDouble = double _
myDouble: Int => Int = <function1>

scala> val amount = myDouble(20)
amount: Int = 40

This time, the explicit function type for myDouble wasn’t required to distinguish it from a function invocation. The underscore (_) served as a placeholder for a future invocation of the function, returning a function value that we could store in myDouble.

Let’s revisit the explicit function type again to explore functions with multiple inputs. A function type with multiple inputs requires explicit parentheses around the input types, which ends up having the appearance of a function definition without parameter names.

Here’s an example of a function value defined with an explicit function type using multiple parameters, enclosed with parentheses:

scala> def max(a: Int, b: Int) = if (a > b) a else b
max: (a: Int, b: Int)Int

scala> val maximize: (Int, Int) => Int = max
maximize: (Int, Int) => Int = <function2>

scala> maximize(50, 30)
res3: Int = 50

We could have also used a wildcard operator here, in place of the explicit type, but this example serves to demonstrate how to specify the multiple parameters in the type.

Finally, here’s a function type that has no inputs. Do the empty parentheses remind you of a certain core Scala type? This is also the literal repesentation of the Unit type (as seen in Table 2-4), which indicates the lack of a value:

scala> def logStart() = "=" * 50 + "\nStarting NOW\n" + "=" * 50
logStart: ()String

scala> val start: () => String = logStart
start: () => String = <function0>

scala> println( start() )
==================================================
Starting NOW
==================================================

This was a gentle introduction to how functions may be treated as data, by storing them in values and assigning them static types. I hope you can try out these examples and gain some familiarity with specifying function types and storing functions in values, because the next few sections on higher-order functions and function literals are going to build on this knowledge and cover some challenging new syntax. Everything’s about to get seriously fun now.

Higher-Order Functions

We have already defined values that have a function type. A higher-order function is a function that has a value with a function type as an input parameter or return value.

Here’s a good use case for a higher-order function: calling other functions that act on a String, but only if the input String is not null. Adding this check can prevent a NullPointerException in the JVM by avoiding a method call on null:

scala> def safeStringOp(s: String, f: String => String) = {
     |   if (s != null) f(s) else s
     | }
safeStringOp: (s: String, f: String => String)String

scala> def reverser(s: String) = s.reverse
reverser: (s: String)String

scala> safeStringOp(null, reverser)
res4: String = null

scala> safeStringOp("Ready", reverser)
res5: String = ydaeR

The call with “null” safely returned the same value back, whereas the call with a valid String returned the reverse of the input value.

This example demonstrated how to pass an existing function as a parameter to a higher-order function. An alternative to using functions as parameters is to define them inline with function literals, as we will see in the next section.

Function Literals

Now we’ll tackle a difficult concept covered by a variety of names by starting with an easy example. In this example we will create a function literal, a working function that lacks a name, and assign it to a new function value:

scala> val doubler = (x: Int) => x * 2
doubler: Int => Int = <function1>

scala> val doubled = doubler(22)
doubled: Int = 44

The function literal in this example is the syntax (x: Int) => x * 2, which defines a typed input argument (x) and the function body (x * 2). Function literals can be stored in function values and variables, or defined as part of a higher-order function invocation. You can express a function literal in any place that accepts a function type.

Although function literals are nameless functions, their concept and the use of the arrow syntax have many names. Here are a few that you may know:

Anonymous functions
Literally true, because function literals do not include a function name. This is the Scala language’s formal name for function literals.
Lambda expressions
Both C# and Java 8 use this term, derived from the original lambda calculus syntax (e.g., x → x*2) in mathematics.
Lambdas
A shortened version of lambda expressions.
function0, function1, function2, ..
The Scala compiler’s term for function literals, based on the number of input arguments. You can see how the single-argument function literal in the preceding example was given the name <function1>.

Why Not Just Call Them Anonymous Functions?

Although The Scala Language Specification (Odersky, 2011) uses the term anonymous function, this term focuses more attention on the lack of a name than on the interesting arrow-based syntax for defining its logic. Thus I prefer the clarifying term function literal, which indicates that the entire logic of a function body is being specified inline! You can think of a function literal as being to a function value what a string literal (e.g., “Hello, World”) is to a string value: a literal expression of the assigned data.

Syntax: Writing a Function Literal

([<identifier>: <type>, ... ]) => <expression>

Let’s define a function value and assign it a new function literal:

scala> val greeter = (name: String) => s"Hello, $name"
greeter: String => String = <function1>

scala> val hi = greeter("World")
hi: String = Hello, World

If you think about it, a function literal is essentially a parameterized expression. We know about expressions that return a value, but now have a way to parameterize their input.

Let’s try a longer example to compare function assignment with function literals. We’ll start with the max() function from the chapter introduction, assign it to a function value, and then reimplement the max() function as a function literal:

scala> def max(a: Int, b: Int) = if (a > b) a else b                        1
max: (a: Int, b: Int)Int

scala> val maximize: (Int, Int) => Int = max                                2
maximize: (Int, Int) => Int = <function2>

scala> val maximize = (a: Int, b: Int) => if (a > b) a else b               3
maximize: (Int, Int) => Int = <function2>

scala> maximize(84, 96)
res6: Int = 96
1

The original max() function

2

.. as assigned to a function value

3

.. as redefined with a function literal

Function literals do not always need input arguments. Let’s try defining one that doesn’t take any arguments. We’ll rewrite another function as a function literal, this time with the logStart() function from the chapter introduction:

scala> def logStart() = "=" * 50 + "\nStarting NOW\n" + "=" * 50
logStart: ()String

scala> val start = () => "=" * 50 + "\nStarting NOW\n" + "=" * 50
start: () => String = <function0>

scala> println( start() )
==================================================
Starting NOW
==================================================

Did you note that the REPL referred to the function literal as a “function0,” its name for input-less functions? This is not the type of the value, however, which was inferred as () => String, an input-less function that returns a string.

As noted, function literals can be defined inside of higher-order function invocations. As an example, we will invoke the “safeStringOp” example (see Higher-Order Functions) with a function literal:

scala> def safeStringOp(s: String, f: String => String) = {
     |   if (s != null) f(s) else s
     | }
safeStringOp: (s: String, f: String => String)String

scala> safeStringOp(null, (s: String) => s.reverse)
res7: String = null

scala> safeStringOp("Ready", (s: String) => s.reverse)
res8: String = ydaeR

The function “safeStringOp” receives a function value parameter named “f” and invokes it conditionally. It makes no distinction between a regular function value being used to invoke it versus our function literal.

In the example, the type of the function parameter “f” is String => String. With this type already defined, we could have removed the explicit type from our function literal, because the compiler could easily infer its expected type. Removing the explicit type means that we could then remove the parentheses from the function literal, because they are unnecessary for single, untyped inputs.

Let’s invoke the “safeStringOp” function again with a function literal that uses this simpler syntax:

scala> safeStringOp(null, s => s.reverse)
res9: String = null

scala> safeStringOp("Ready", s => s.reverse)
res10: String = ydaeR

The function literals here, stripped of their explicit types or parentheses, are reduced down to the basic essence of the function. They take an input parameter and return a value based on an operation on that parameter.

Although these function literals are very simple expressions of functions, Scala supports even simpler expressions with placeholder syntax.

Placeholder Syntax

Placeholder syntax is a shortened form of function literals, replacing named parameters with wildcard operators (_). It can be used when (a) the explicit type of the function is specified outside the literal and (b) the parameters are used no more than once.

Here is an example of a doubling function literal using wildcard operators in place of named parameters:

scala> val doubler: Int => Int = _ * 2
doubler: Int => Int = <function1>

Placeholder syntax is valid here because the input parameter is only used once and the literal’s type has an external explicit definition (in the value).

As another example, let’s invoke the “safeStringOp” example with placeholder syntax:

scala> def safeStringOp(s: String, f: String => String) = {
     |   if (s != null) f(s) else s
     | }
safeStringOp: (s: String, f: String => String)String

scala> safeStringOp(null, _.reverse)
res11: String = null

scala> safeStringOp("Ready", _.reverse)
res12: String = ydaeR

The body of the function literal is operationally the same as s => s.reverse, but simplified with placeholder syntax. The reference to the input parameter s has been replaced with a wildcard (_) representing the first input parameter to the function. Essentially the wildcard is the single String input parameter.

Let’s demonstrate how this ordering of placeholders works by trying an example with two placeholders:

scala> def combination(x: Int, y: Int, f: (Int,Int) => Int) = f(x,y)
combination: (x: Int, y: Int, f: (Int, Int) => Int)Int

scala> combination(23, 12, _ * _)
res13: Int = 276

The use of two placeholders admittedly makes the syntax more abstract. Try to keep in mind that they are positionally replacing the input parameters (x and y, respectively).

Using an additional placeholder here would result in an error, because the number of placeholders must match the number of input arguments. If I call the reduce method with one or three placeholders, an error would ensue.

Let’s kick the number of placeholders up for the last time, from two to three. Is this example still easily readable?

scala> def tripleOp(a: Int, b: Int, c: Int, f: (Int, Int, Int) => Int) = f(a,b,c)
tripleOp: (a: Int, b: Int, c: Int, f: (Int, Int, Int) => Int)Int

scala> tripleOp(23, 92, 14, _ * _ + _)
res14: Int = 2130

The tripleOp function takes four parameters: three Int values and a function that can reduce them down to a single Int. The actual function body is far shorter than the parameter list, and applies the function to the input values.

This example function, tripleOp, is limited to integer values. However, wouldn’t it be more useful if it was generic and supported type parameters?

Let’s redefine the tripleOp function using two type parameters, one for the common input type and one for the single return value type. This will give us some flexibility to call the tripleOp function with any type of inputs or anonymous functions that we choose (as long as the anonymous function takes three inputs):

scala> def tripleOp[A,B](a: A, b: A, c: A, f: (A, A, A) => B) = f(a,b,c)
tripleOp: [A, B](a: A, b: A, c: A, f: (A, A, A) => B)B

scala> tripleOp[Int,Int](23, 92, 14, _ * _ + _)
res15: Int = 2130

scala> tripleOp[Int,Double](23, 92, 14, 1.0 * _ / _ / _)
res16: Double = 0.017857142857142856

scala> tripleOp[Int,Boolean](93, 92, 14, _ > _ + _)
res17: Boolean = false

This syntax is admittedly a bit difficult to comprehend if you aren’t experienced with Scala. Eventually, after reading the material and working on the exercises at the end of this chapter, the use of placeholders will feel like just another tool to you.

Placeholder syntax is especially helpful when working with data structures and collections. Many of the core sorting, filtering, and other data structure methods tend to use first-class functions, and placeholder syntax reduces the amount of extra code required to call these methods.

Partially Applied Functions and Currying

Invoking functions, both regular and higher-order, typically requires specifying all of the function’s parameters in the invocation (the exception being those functions with default parameter values). What if you wanted to reuse a function invocation and retain some of the parameters to avoid typing them in again?

To demonstrate this answer, I’ll use the example of a two-parameter function that checks if a given number is a factor of the other number:

scala> def factorOf(x: Int, y: Int) = y % x == 0
factorOf: (x: Int, y: Int)Boolean

If you want a shortcut to the function without retaining any parameters, you can use the wildcard operator (_) assignment we covered in this chapter’s introduction:

scala> val f = factorOf _
f: (Int, Int) => Boolean = <function2>

scala> val x = f(7, 20)
x: Boolean = false

If you want to retain some of the parameters, you can partially apply the function by using the wildcard operator to take the place of one of the parameters. The wildcard operator here requires an explicit type, because it is used to generate a function value with a declared input type:

scala> val multipleOf3 = factorOf(3, _: Int)
multipleOf3: Int => Boolean = <function1>

scala> val y = multipleOf3(78)
y: Boolean = true

The new function value, multipleOf3, is a partially applied function, because it contains some but not all of the parameters for the factorOf() function.

A cleaner way to partially apply functions is to use functions with multiple parameter lists. Instead of breaking up a parameter list into applied and unapplied parameters, apply the parameters for one list while leaving another list unapplied. This is a technique known as currying the function:

scala> def factorOf(x: Int)(y: Int) = y % x == 0
factorOf: (x: Int)(y: Int)Boolean

scala> val isEven = factorOf(2) _
isEven: Int => Boolean = <function1>

scala> val z = isEven(32)
z: Boolean = true

In terms of a function type, a function with multiple parameter lists is considered to be a chain of multiple functions. Each separate parameter list is considered to be a separate function call.

Our example function def factorOf(x: Int, y: Int) has the function type (Int, Int) => Boolean. But the updated example function “def factorOf(x: Int)(y: Int)” has the function type Int => Int => Boolean. When curried, the function type becomes the second chained function, Int => Boolean. In the preceding example, the function value “isEven” curries the first part of the chained function with the integer value 2.

With some ingenuity you could write your own function literals that handle the job that partially applied functions and curried functions provide. Retaining a reusable parameter in a function literal and invoking a new function with it and new parameters isn’t a complex trick. The benefit that partially applied functions and curried functions provide is an expressive syntax for doing so.

By-Name Parameters

We have studied higher-order functions that take a function value as a parameter. An alternate form of a function type parameter is a by-name parameter, which can take either a value or a function that eventually returns the value. By supporting invocations with both values and functions, a function that takes a by-name parameter leaves the choice of which to use up to its callers.

Syntax: Specifying a By-Name Parameter

<identifier>: => <type>

Each time a by-name parameter is used inside a function, it gets evaluated into a value. If a value is passed to the function then there is no effect, but if a function is passed then that function is invoked for every usage.

When you pass a function to a by-name parameter, make sure that you understand any cost of repeated accesses of your function. For example, an expression that searches a database and returns its value may have acceptable performance if used once to pass a fixed value to a function. But if that expression is used for a by-name parameter, it becomes a function value that is invoked every time the parameter is accessed in the method.

The main benefit of using by-name parameters, as opposed to value or function parameters, is the flexibility they provide. Functions that take by-name parameters can be used when values are available, and also when a function needs to be used instead. Although multiple parameter accesses implies multiple invocations of the function parameter, the inverse is also true. A function passed in a by-name parameter will not be invoked if the parameter is not accessed, so a costly function call can be avoided if necessary.

Let’s try invoking a function that has a by-name parameter. We’ll use it with a regular value and then with a function to verify that it invokes the function every time the parameter is accessed:

scala> def doubles(x: => Int) = {
     |   println("Now doubling " + x)                                       1
     |   x * 2
     | }
doubles: (x: => Int)Int

scala> doubles(5)                                                           2
Now doubling 5
res18: Int = 10

scala> def f(i: Int) = { println(s"Hello from f($i)"); i }
f: (i: Int)Int

scala> doubles( f(8) )                                                      3
Hello from f(8)
Now doubling 8
Hello from f(8)                                                             4
res19: Int = 16
1

The x by-name parameter is accessed here just like a normal by-value parameter.

2

Invoke the doubles method with a regular value and it will operate normally.

3

…but when you invoke it with a function value, that function value will get invoked inside the doubles method.

4

Because the double method refers to the x param twice, the “Hello” message gets invoked twice.

Partial Functions

All of the functions we have studied so far are known as total functions, because they properly support every possible value that meets the type of the input parameters. A simple function like def double(x: Int) = x*2 can be considered a total function; there is no input x that the double() function could not process.

However, there are some functions that do not support every possible value that meets the input types. For example, a function that returns the square root of the input number would certainly not work if the input number was negative. Likewise, a function that divides by a given number isn’t applicable if that number is zero. Such functions are called partial functions because they can only partially apply to their input data.

Scala’s partial functions are function literals that apply a series of case patterns to their input, requiring that the input match at least one of the given patterns. Invoking one of these partial functions with data that does not meet at least one case pattern results in a Scala error.

What Is the Difference Between Partial and Partially Applied Functions?

The two terms look and sound almost the same, causing many developers to mix them up. A partial function, as opposed to a total function, only accepts a partial amount of all possible input values. A partially applied function is a regular function that has been partially invoked, and remains to be fully invoked (if ever) in the future.

Let’s take one of the examples of match expressions (from Match Expressions) and reuse it as a new partial function:

scala> val statusHandler: Int => String = {
     |   case 200 => "Okay"
     |   case 400 => "Your Error"
     |   case 500 => "Our error"
     | }
statusHandler: Int => String = <function1>

We now have a function literal that is only applicable to integers with the values 200, 400, and 500. We’ll test it out with valid inputs first:

scala> statusHandler(200)
res20: String = Okay

scala> statusHandler(400)
res21: String = Your Error

What do you expect to happen if we call it with an integer that doesn’t match one of its case patterns?

scala> statusHandler(401)
scala.MatchError: 401 (of class java.lang.Integer)
  at $anonfun$1.apply(<console>:7)
  at $anonfun$1.apply(<console>:7)
  ... 32 elided

A MatchError resulted because the input value, while having the correct Int type, did not match any of the partial function’s case patterns.

Partial functions may seem like an odd feature, because when they are not applicable they can lead to errors like this. One method to prevent such errors is to use a wildcard pattern at the end to catch all other errors, but then the term “partial function” wouldn’t really be applicable. You’ll find partial functions more useful when working with collections and pattern matching. For example, you can “collect” every item in a collection that is accepted by a given partial function.

Invoking Higher-Order Functions with Function Literal Blocks

We covered how to invoke functions with expression blocks instead of parentheses or spaces (see Function Invocation with Expression Blocks). You can reuse this notation with higher-order functions, invoking them with function literal blocks in addition to or in place of parentheses. A function invoked by its name and a large expression block takes the block as a function literal, which can then be invoked zero or more times. A common use of this syntax is to invoke utility functions with an expression block. For example, a higher-order function can wrap a given expression block in a single database session or transaction.

I’ll use the “safeStringOps” function to demonstrate when this syntax may be desirable and how to use it. To start, here is the “safeStringOps” function used with a regular function literal, before converting it to the desired syntax:

scala> def safeStringOp(s: String, f: String => String) = {
     |   if (s != null) f(s) else s
     | }
safeStringOp: (s: String, f: String => String)String

scala> val uuid = java.util.UUID.randomUUID.toString                    1
uuid: String = bfe1ddda-92f6-4c7a-8bfc-f946bdac7bc9

scala> val timedUUID = safeStringOp(uuid, { s =>
     |   val now = System.currentTimeMillis                             2
     |   val timed = s.take(24) + now                                   3
     |   timed.toUpperCase
     | })
timedUUID: String = BFE1DDDA-92F6-4C7A-8BFC-1394546043987
1

A UUID utility in Java’s java.util package, accessible (as are all JDK classes) from Scala.

2

System.currentTimeMillis provides the epoch time (elapsed time since January 1, 1970 GMT) in milliseconds, useful for creating timestamps.

3

The take(x) method returns the first x items from the String, in this case the first four sections of the UUID.

In this example, a multiline function literal is passed along with a value parameter to a function. This works, but including these together in the same parenthesis block is unwieldy.

We can improve this by splitting the parameters in “safeStringOp” into two separate groups (see Parameter Groups). The second parameter group, containing the function type, can then be invoked with expression block syntax:

scala> def safeStringOp(s: String)(f: String => String) = {
     |   if (s != null) f(s) else s
     | }
safeStringOp: (s: String)(f: String => String)String

scala> val timedUUID = safeStringOp(uuid) { s =>
     |   val now = System.currentTimeMillis
     |   val timed = s.take(24) + now
     |   timed.toUpperCase
     | }
timedUUID: String = BFE1DDDA-92F6-4C7A-8BFC-1394546915011

We now have a cleaner invocation of safeStringOp, passing it the value parameter in parentheses and the function parameter as a free-standing function literal block.

Here is an alternate example, one that takes a single by-name parameter. We’ll make the function more generic with a type parameter used for the by-name parameter return type and the main function’s return type:

scala> def timer[A](f: => A): A = {                                         1
     |   def now = System.currentTimeMillis                                 2
     |   val start = now; val a = f; val end = now
     |   println(s"Executed in ${end - start} ms")
     |   a
     | }
timer: [A](f: => A)A

scala> val veryRandomAmount = timer {                                       3
     |   util.Random.setSeed(System.currentTimeMillis)
     |   for (i <- 1 to 100000) util.Random.nextDouble                      4
     |   util.Random.nextDouble
     | }
Executed in 13 ms
veryRandomAmount: Double = 0.5070558765221892
1

The type parameter “A” helps the return type of the “f” by-name parameter become the return type of the “timer” function, reducing the impact of wrapping code with the “timer” function.

2

This inner, nested function is here for purely aesthetic reasons, enabling us to retrieve the current millisecond amount compactly.

3

Finally, we have reduced the expression block syntax for higher-order functions to its simplest form: the function name and the block. You can view the code between the braces as being an expression block, or as a function literal block, or as regular code being wrapped by the “timer” function.

4

This line generates and discards 100,000 random floating-point numbers. It’s useful for running out the clock for a timing demonstration, but I wouldn’t recommend using this in production code.

The “timer” function is used here to wrap a discrete unit of code, but it could also be integrated into an existing code base. You could use it to wrap the last part of any function, measuring its performance while ensuring the function’s return value passes from the code block through “timer” and is returned by the function.

Functions that can wrap indiscriminate blocks of code with utilities in this way are a major benefit of using the “expression block” style of higher-order function invocations. Some of the other uses for this invocation style include:

  • Managing database transactions, where the higher-order function opens the session, invokes the function parameter, and then closes the transaction with a commit or rollback.
  • Handling expected errors with retries, by calling the function parameter a set number of times until it stops causing errors.
  • Conditionally invoking the function parameter based on local, global, or external values (e.g., a database setting or environment variable).

As with many other features in Scala, you have more than one way to invoke higher-order functions. I find the use of this syntax to be a clean break from using the traditional parentheses, but the most important criteria for when and where to use it is if it seems right to you.

Summary

Scala treats functions as first-class data types, as demonstrated throughout this chapter and supported by the notions of higher-order functions, function literals, and function types. While simply stated, until you have some experience working with first-class functions you may find the concept a difficult one to understand. If you haven’t already done so, I highly recommend trying out the code samples and experimenting with writing your own first-class function-based code. And then, after you have become familiar with storing functions as data and using them to invoke higher-order functions, you’ll find the following exercises will help to increase your comfort level with this challenging topic.

The real beauty and utility of higher-order functions, however, cannot be demonstrated with the data types we have thus far learned. To really demonstrate them we will need to learn a critical component of writing any kind of useful and data-driven code. I’m talking about collections, data structures that scale from zero to many elements and make it possible to collect multiple values of a given type. From lists to maps, Scala not only supports the data structures that you’re well familiar with, but provides ample use of higher-order functions to maximize your productivity. We’ll cover not only creating and iterating through collections, but how you’ll use map(), reduce(), and filter() to manage them with amazingly expressive code.

From this point forward you can expect to see first-class functions and higher-order functions play a prominent role in code examples and exercises. Whether demonstrated with the data types we have learned thus far or with the higher-order function-based collections library, these are the shining stars of the Scala language.

Exercises

  1. Write a function literal that takes two integers and returns the higher number. Then write a higher-order function that takes a 3-sized tuple of integers plus this function literal, and uses it to return the maximum value in the tuple.
  2. The library function util.Random.nextInt returns a random integer. Use it to invoke the “max” function with two random integers plus a function that returns the larger of two given integers. Do the same with a function that returns the smaller of two given integers, and then a function that returns the second integer every time.
  3. Write a higher-order function that takes an integer and returns a function. The returned function should take a single integer argument (say, “x”) and return the product of x and the integer passed to the higher-order function.
  4. Let’s say that you happened to run across this function while reviewing another developer’s code:

    def fzero[A](x: A)(f: A => Unit): A = { f(x); x }

    What does this function accomplish? Can you give an example of how you might invoke it?

  5. There’s a function named “square” that you would like to store in a function value. Is this the right way to do it? How else can you store a function in a value?

    def square(m: Double) = m * m
    val sq = square
  6. Write a function called “conditional” that takes a value x and two functions, p and f, and returns a value of the same type as x. The p function is a predicate, taking the value x and returning a Boolean b. The f function also takes the value x and returns a new value of the same type. Your “conditional” function should only invoke the function f(x) if p(x) is true, and otherwise return x. How many type parameters will the “conditional” function require?
  7. Do you recall the “typesafe” challenge from the exercises in Chapter 3? There is a popular coding interview question I’ll call “typesafe,” in which the numbers 1-100 must be printed one per line. The catch is that multiples of 3 must replace the number with the word “type,” while multiples of 5 must replace the number with the word “safe.” Of course, multiples of 15 must print “typesafe.”

    Use the “conditional” function from exercise 6 to implement this challenge.

    Would your solution be shorter if the return type of “conditional” did not match the type of the parameter x? Experiment with an altered version of the “conditional” function that works better with this challenge.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required