I understand the basic concept of call-by-name and call-by-value, and I have also looked into handful number of examples. However, I am not very clear about when to use call-by-
There are plenty of places were call-by-name may gain performance or even correctness.
Simple performance example: logging. Imagine an interface like this:
trait Logger {
def info(msg: => String)
def warn(msg: => String)
def error(msg: => String)
}
And then used like this:
logger.info("Time spent on X: " + computeTimeSpent)
If the info
method doesn't do anything (because, say, the logging level was configured for higher than that), then computeTimeSpent
never gets called, saving time. This happens a lot with loggers, where one often sees string manipulation which can be expensive relative to the tasks being logged.
Correctness example: logic operators.
You have probably seen code like this:
if (ref != null && ref.isSomething)
Say you declared &&
method like this:
trait Boolean {
def &&(other: Boolean): Boolean
}
then, whenever ref
is null
, you'll get an error because isSomething
will be called on a null
reference before being passed to &&
. For this reason, the actual declaration is:
trait Boolean {
def &&(other: => Boolean): Boolean =
if (this) other else this
}
So one may actually wonder is when to use call-by-value. In fact, in the Haskell programming language everything works similar to how call-by-name works (similar, but not the same).
There are good reasons not to use call-by-name: it is slower, it creates more classes (meaning the program takes longer to load), it consumes more memory, and it is different enough that many have difficult reasoning about it.