Scala Functions - Call-by-Name

Learn via video courses
Topics Covered

Overview

In Scala, parameters can be passed to functions using different evaluation strategies. One of these strategies is called call by name, which allows us to pass a code block that will be evaluated each time the parameter is used within the function. This can be contrasted with call by value, where the parameter is evaluated only once before entering the function.

What is Call-by-Name?

Scala, provides us with various methods for parameter passing when invoking functions. Call by name introduces a distinct approach to handling function arguments and their evaluation. Unlike call by value, call by name delays the computation of function arguments until they are explicitly referenced within the function body. In call by name, the argument is effectively replaced with its expression, creating a sort of lazy evaluation mechanism.

This dynamic evaluation strategy provides a unique advantage, allowing functions to efficiently work with expressions, particularly those involving potentially expensive computations or side effects. By deferring evaluation until actual use, call by name promotes concise and expressive code while enabling advanced programming techniques.

Syntax

The syntax of "call by name" in Scala involves using the => symbol before the parameter type in a function definition. This indicates that the corresponding parameter is being passed by name.

Function Definition:

Start by defining a function with a parameter that we want to pass by name. The parameter should be preceded by the "=>" symbol.

Function Body:

Inside the function body, we can use the call-by-name parameter just like any other parameter. However, the key difference is that the expression corresponding to the parameter is not evaluated immediately; it will be evaluated each time it is used within the function body.

Function Invocation:

When we call a function with call-by-name parameters, we can pass regular values or expressions as arguments. The expressions are not evaluated immediately; they are evaluated only when they are referenced within the function.

Example: How to Implement Call–by–name?

In this example, we'll create a simple function that showcases how call by name works by delaying the evaluation of an expression until it's used within the function body.

Suppose we want to create a function that prints a message along with the current timestamp. We want the timestamp to be computed only when it's used within the function.

Code:

Output:

Explanation:

  • Inside the main method we have called the printTimestamp function and passed the result of the getTimestamp() expression as an argument. Note that the getTimestamp() expression is not evaluated yet.
  • The printTimestamp function is defined with a call-by-name parameter named timestamp. This means that the timestamp parameter will be evaluated each time it's used within the function.
  • The moment we reference timestamp inside the printTimestamp function body, the getTimestamp() expression is evaluated to fetch the current timestamp.
  • The getTimestamp() function returns the current timestamp using System.currentTimeMillis().
  • We can observe that the message "Getting current timestamp" is printed only when the getTimestamp() expression is used inside the printTimestamp function. This demonstrates the delayed evaluation characteristic of call by name.

Call By-Value or Call By-Name

Let us compare both of them in different aspects.

AspectCall by Value (CBV)Call by Name (CBN)
Evaluation StrategyArgument is evaluated before entering the function.Argument is not evaluated until explicitly used.
Lazy EvaluationNot inherently lazy; expressions computed only once.Supports lazy evaluation, deferring computation.
PerformanceEfficient for frequently used arguments.Efficient for infrequently used or costly arguments.
Side EffectsSide effects occur before function entry.Side effects occur when expressions are evaluated.
Control StructuresConsistent behavior; expressions evaluated once.Expressions evaluated on demand; e.g., short-circuit.
DSLsCan be used, but may be less intuitive in some cases.Supports intuitive and expressive embedded DSLs.
Deterministic OrderOrder is well-defined and consistent.Order depends on actual usage within the function.

Conclusion

  • Call by name is a parameter-passing mechanism in Scala where function arguments are not evaluated until explicitly referenced within the function body, allowing for lazy evaluation and optimized handling of potentially expensive computations.
  • Call by name in Scala allows for efficient lazy evaluation of expressions, optimizing performance by computing values only when needed, and enhancing code flexibility in scenarios involving complex computations or side effects.
  • Call by value evaluates function arguments before entering the function, while call by name delays evaluation until arguments are used within the function body, enabling lazy computation and potential optimization.