CHAPTER 4 ■ FUN WITH FUNCTIONS, AND NEVER HAVING TO CLOSE THAT JDBC CONNECTION
107
The buildCalc method returns a function that can be passed to other functions. Also,
the JVM can optimize the composed functions so that they perform better than the inter-
preted version. The performance of the composed function is better because there is no
overhead associated with pattern matching each element. The function is evaluated by
repeatedly calling the function’s
apply method. Thus, the cost of each node is one or two
method dispatches rather than the cost of the pattern matching.
Let’s turn to other ways that functions can help us improve performance and
readability.
Call-by-Name, Call-by-Value, and General
Laziness
In Java programs, when you call a method with parameters, the value of the parameters
are all calculated before the method is called. Thus, in
foo(1 + 1, "A String".length());
the expressions 1 + 1 and "A String".length() are both evaluated before the call to foo
is made. This is usually what we want. However, there are some cases when we want to
parameters to be optionally evaluated or repeatedly evaluated. In these cases, Scala
provides the call-by-name mechanism. There’s no syntactic difference to the caller for
call-by-name parameters.
The first example for call-by-name is the logging example. It’s very computationally
costly to calculate log messages simply to discard them if the message is not going to be
logged. This is very common in Java code:
if (logger.level().intValue() >= INFO.intValue()) {
logger.log(INFO, "The value is "+value);
}
In this code, we have to push the decision to evaluate logger.log(INFO, "The value is
"+value); into the place where we call logger. This means we need to wrap the call to
logger in an if statement. It would be much better from a coding perspective if the cost of
evaluating the
String to be logged were incurred only if the String is going to be logged
and if the current log level is known to and tested by the code inside
logger rather than in
the call to
logger. Call-by-name gives us the ability to delay the evaluation of the String to
log only if that
String will actually be logged.
In Scala, we can define a
log method that takes the thing to log as call-by-name:
def log(level: Level, msg: => String) =
if (logger.level.intValue >= level.intValue) logger.log(level, msg)
19897ch04.fm Page 107 Thursday, April 23, 2009 4:35 PM