- 12.1 Functions as Values
- 12.2 Anonymous Functions
- 12.3 Parameters That Are Functions
- 12.4 Parameter Inference
- 12.5 Useful Higher-Order Functions
- 12.6 Closures
- 12.7 Interoperability with Lambda Expressions
- 12.8 Currying
- 12.9 Methods for Composing, Currying, and Tupling
- 12.10 Control Abstractions
- 12.11 Nonlocal Returns
- Exercises
12.10 Control Abstractions
In Scala, one can model a sequence of statements as a function with no parameters or return value. For example, here is a function that runs some code in a thread:
def runInThread(block: () => Unit) = Thread(() => block()).start()
The code is given as a function of type () => Unit. However, when you call this function, you need to supply an unsightly () =>:
runInThread { () => println("Hi"); Thread.sleep(10000); println("Bye") }
To avoid the () => in the call, use the call by name notation: Omit the (), but not the =>, in the parameter declaration and in the call to the parameter function:
def runInThread(block: => Unit) = Thread(() => block).start()
Then the call becomes simply
runInThread { println("Hi"); Thread.sleep(10000); println("Bye") }
This looks pretty nice. Scala programmers can build control abstractions: functions that look like language keywords. For example, we can implement a function that is used exactly as a while statement. Or, we can innovate a bit and define an until statement that works like while, but with an inverted condition:
def until(condition: => Boolean)(block: => Unit): Unit = if !condition then block until(condition)(block)
Here is how you use until:
var x = 10 until (x == 0) { x -= 1 println(x) }
The technical term for such a function parameter is a call-by-name parameter. Unlike a regular (or call-by-value) parameter, the parameter expression is not evaluated when the function is called. After all, we don’t want x == 0 to evaluate to false in the call to until. Instead, the expression becomes the body of a function with no parameters. That function is passed to until.
Look carefully at the until function definition. Note that it is curried: It first consumes the condition, then the block as a second parameter. Without currying, the call would look like this:
until(x == 0, { ... })
which wouldn’t be as pretty.