Scala Functions - Call-by-Name Parameters



This chapter takes you through the concept of call-by-name parameters in Scala programming. You can delay the evaluation of an expression until it is actually used within the function. It is used where you want to defer computation and avoid unnecessary evaluations.

Call-by-Name Parameters

Functions can have parameters that are either call-by-value or call-by-name. Call-by-value parameters are evaluated before the function is executed. Whereas call-by-name parameters are evaluated each time these are accessed within the function. Call-by-name parameters are defined using the => syntax.

Definition

Call-by-name parameters in Scala are defined using the => symbol before the parameter type. So, parameters are evaluated lazily. Hence expression is not computed until it is actually referenced within the function body.

Syntax

The syntax of the call by name parameter is -

def functionName(parameterName: => Type): ReturnType = {
   // Function body
}

Example

The following example demonstrates a simple function with a call-by-name parameter in Scala programming -

object Demo {
   def main(args: Array[String]) = {
      delayed(time())
   }

   def time(): Long = {
      println("Getting time in nanoseconds")
      System.nanoTime()
   }

   def delayed(t: => Long): Unit = {
      println("In delayed method")
      println("Param: " + t)
      println("Param: " + t)
   }
}

Save the above program in Demo.scala. Use the following commands to compile and execute this program.

Command

> scalac Demo.scala
> scala Demo

Output

In delayed method
Getting time in nanoseconds
Param: <some nanosecond timestamp>
Getting time in nanoseconds
Param: <some nanosecond timestamp>

In the example, the time method returns the current time in nanoseconds. The delayed method takes call-by-name parameter t of type Long. The time method is called each time parameter t is accessed within the delayed method. Hence multiple evaluations occur.

Differences Between Call-by-Name and Call-by-Value

Call-by-value parameters are evaluated before the function execution begins. This method is more efficient because it avoids the repetitive re-evaluation of expressions required by call-by-name parameters. Whereas, call-by-name parameters are evaluated each time these are accessed within the function. So, this can lead to inefficiencies if the parameter is used multiple times.

  • Call-by-Value: Parameters are evaluated before the function is executed.
  • Call-by-Name: Parameters are evaluated each time they are accessed within the function.

Syntax Comparison

The syntax comparison of these -

// Call-by-Value
def functionName(parameterName: Type): ReturnType = {
   // Function body
}

// Call-by-Name
def functionName(parameterName: => Type): ReturnType = {
   // Function body
}

Example

The following example compares call-by-value and call-by-name parameters in Scala programming -

object Demo {
   def main(args: Array[String]) = {
      println("Call-by-Value:")
      callByValue(time())
      println("Call-by-Name:")
      callByName(time())
   }

   def time(): Long = {
      println("Getting time in nanoseconds")
      System.nanoTime()
   }

   def callByValue(t: Long): Unit = {
      println("In callByValue method")
      println("Param: " + t)
      println("Param: " + t)
   }

   def callByName(t: => Long): Unit = {
      println("In callByName method")
      println("Param: " + t)
      println("Param: " + t)
   }
}

Save the above program in Demo.scala. Use the following commands to compile and execute this program.

Command

> scalac Demo.scala
> scala Demo

Output

Call-by-Value:
Getting time in nanoseconds
In callByValue method
Param: <some nanosecond timestamp>
Param: <some nanosecond timestamp>
Call-by-Name:
In callByName method
Getting time in nanoseconds
Param: <some nanosecond timestamp>
Getting time in nanoseconds
Param: <some nanosecond timestamp>

In the example, the callByValue method evaluates the time function once before executing method. So the same value is printed twice as a result. The callByName method evaluates time function every time the parameter t is accessed. So the different values are printed as a result.

Use of Call-by-Name Parameters

Call-by-name parameters have various uses, like in implementation of lazy evaluation and custom control structures. In lazy evaluation, expressions are only evaluated when their values are needed. So, it can enhance performance by deferring computation. For control structures, call-by-name, developers can create custom loops and conditionals that dictate the flow of execution based on when parameters are accessed.

1. Lazy Evaluation

Call-by-name parameters can be used to implement lazy evaluation, where expressions are only evaluated when needed.

Example

object Demo {
   def main(args: Array[String]) = {
      lazyEval(println("This will not be printed"))
   }

   def lazyEval(expr: => Unit): Unit = {
      println("In lazyEval method")
   }
}

Save the above program in Demo.scala. Use the following commands to compile and execute this program.

Command

> scalac Demo.scala
> scala Demo

Output

In lazyEval method

In the example, the lazyEval method takes a call-by-name parameter expr of type Unit. Since the parameter is never accessed within the method. So the println statement in the main method is not executed.

2. Control Structures

Custom control structures (like loops and conditionals) can be implemented using call-by-name parameters to control the flow of execution.

Example

object Demo {
   def main(args: Array[String]) = {
      loopWhile(5 > 3) {
         println("Looping...")
      }
   }

   def loopWhile(condition: => Boolean)(body: => Unit): Unit = {
      if (condition) {
         body
            loopWhile(condition)(body)
      }
   }
}

Save the above program in Demo.scala. Use the following commands to compile and execute this program.

Command

> scalac Demo.scala
> scala Demo

Output

Looping...
Looping...
Looping...
...

In the example, the loopWhile method implements custom control structure using call-by-name parameters for the condition and body of the loop. The loop executes as long as the condition is true.

Call-by-Name Parameters Summary

  • Call-by-name parameters delay the evaluation of an expression until it is accessed within the function.
  • Call-by-name parameters are defined using the =>
  • Call-by-name parameters can be useful for lazy evaluation and implementing custom control structures.
Advertisements