Learning Scala and functional programming in general. In the following tail-recursive factorial implementation:
def factorialTailRec(n: Int) : Int = {
@tailrec
def factorialRec(n: Int, f: => Int): Int = {
if (n == 0) f else factorialRec(n - 1, n * f)
}
factorialRec(n, 1)
}
I wonder whether there is any benefit to having the second parameter called by value vs called by name (as I have done). In the first case, every stack frame is burdened with a product. In the second case, if my understanding is correct, the entire chain of products will be carried over to the case if ( n== 0) at the nth stack frame, so we will still have to perform the same number of multiplications. Unfortunately, this is not a product of form a^n, which can be calculated in log_2n steps through repeated squaring, but a product of terms that differ by 1 every time. So I can't see any possible way of optimizing the final product: it will still require the multiplication of O(n) terms.
Is this correct? Is call by value equivalent to call by name here, in terms of complexity?
Through experimentation I found out that with the call by name formalism, the method becomes... non-tail recursive! I made this example code to compare factorial tail-recursively, and factorial non-tail-recursively:
Observe that the inner
tailRecfunction calls the second argument by name. for which the@tailRecannotation still does NOT throw a compile-time error!I've been playing around with different values for the
ITERSvariable, and for a value of 100,000, I receive a...StackOverflowError!(The result of zero is there because of overflow of
Int.)So I went ahead and changed the signature of
factorialTailRec/2, to:i.e call by value for the argument
f. This time, the portion ofmainthat runsfactorialTailRecfinishes absolutely fine, whereas, of course,factorial/1crashes at the exact same integer.Very, very interesting. It seems as if call by name in this situation maintains the stack frames because of the need of computation of the products themselves all the way back to the call chain.