CBV and CBN


Call by Value vs. Call by Name: Which One is Faster?

Now that we understand evaluation strategies, let’s explore the two primary approaches:

To make this comparison concrete, let’s analyze the function given in the slide:

def test(x: Int, y: Int): Int =
  x * x

This function only uses x — the parameter y is completely ignored.

Now, let’s evaluate different function calls using CBV and CBN and see which is faster.


Understanding the Key Difference

This means that if an argument is expensive to compute and not needed, Call by Name can save time by skipping unnecessary computations.


Evaluating Function Calls

test(2, 3)
test(2, 3)
→ 2 * 2
→ 4
test(2, 3)
→ 2 * 2
→ 4

So, in this case, both CBV and CBN produce the same result... but what if we had a more complex function?


test(6 + 9, 8)
test(6 + 9, 8)
→ test(15, 8)  // argument (6 + 9) evaluated before function call
→ 15 * 15
→ 225
test(6 + 9, 8)
→ (6 + 9) * (6 + 9)  // argument is substituted directly
→ 15 * (6 + 9)
→ 15 * 15
→ 225

CBV is more efficient here because it avoids redundant calculations.


test(4, 2 * 10)
test(4, 2 * 10)
→ test(4, 20)  // Arguments evaluated before function call
→ 4 * 4
→ 16
test(4, 2 * 10)
→ 4 * 4
→ 16

CBN is more efficient here because it avoids computing 2 * 10, which is not needed.


test(3 + 2, 4 * 3)
test(3 + 2, 4 * 3)
→ test(5, 4 * 3)
→ test(5, 12)  // arguments evaluated before function call
→ 5 * 5
→ 25
test(3 + 2, 4 * 3)
→ (3 + 2) * (3 + 2)  // argument is substituted directly
→ 5 * (3 + 2)
→ 5 * 5
→ 25

Both strategies are equally efficient in this case.


Conclusion

Function Call Call by Value Call by Name Same
test(2, 3)
test(6 + 9, 8)
test(4, 2 * 10)
test(3 + 2, 4 * 3)

One more example

The previous examples focused on pure functions where evaluation order only affected performance. But what happens when a function has side effects, such as printing output? This is where the difference between Call by Value (CBV) and Call by Name (CBN) becomes even more obvious.

Let’s define a simple function that prints something when called and returns an Int:

def something(): Int = {
  println("calling something")
  1
}

Now, we define two functions that accept an Int argument:

def callByValue(x: Int) = {
  println("x1=" + x)
  println("x2=" + x)
}

// note the `⇒` syntax for CBN!
def callByName(x: ⇒ Int) = { 
  println("x1=" + x)
  println("x2=" + x)
}

Now, let’s see what happens when we call them with something().

Call by Value

callByValue(something())

Output:

calling something
x1=1
x2=1

Call by Name

callByName(something())

Output:

calling something
x1=1
calling something
x2=1

Key Takeaways:

This example demonstrates why understanding when arguments are evaluated is crucial, especially when dealing with functions that produce side effects.

Source: CBN vs CBV, clarification needed