def factorial(n: Int): Int = {
def loop(n: Int, acc: Int): Int = if (n > 0) loop(n -1, n * acc) else acc
loop(n, 1)
}
could be written as:
def factorial(n: Int, private acc: Int = 1) : Int = if (n > 0) factorial(n -1, n * acc) else acc
Private parameters must have a default value, which is used on first entry to the method. The private parameter is not visible externally, so factorial’s signature is a single Int parameter as expected. The default value can not be used inside the method and must be written explicitly.
Interesting idea! One problem I could see is that the compiler can make loop tail-recursive, while factorial could only be tail-recursive if it’s final or it’s enclosing class is final (I think?)
I have had the thought for a while, I posted that example yesterday as I’ve been working my way through “Functional Programming in Scala” again and came across it. That simple example could actually be done with a foldLeft, but there are more complex examples with multiple internal parameters where it would be useful. I don’t feel using tuples with folds is really ideal.
I don’t think such a language feature would pull its weight in terms of complexity cost versus utility. It would only be used in some cases where the implementation of a method happens to immediately defer to another private implementation detail with more parameters, without any postprocessing.
When one looks at textbook examples for tail recursion, one can indeed encounter a few cases. But in practice I find this scenario to be extremely rare.
I find this case to be extremely common; probably upwards of 80% of my recursive methods are of this type. It’s also the most common reason for me to use while loops instead of recursion: I need initial parameters, at which point it’s easier to write the loop than an inner method.
I wonder what you’re writing that is so different from what I’m writing? For instance, suppose I want to look through an array to find a pair of specified numbers separated by a gap of one and return the index. I could
def omgSoSlowAndNoneTooClear(xs: Array[Int], n: Int): Int =
xs.sliding(3).iterator.
filter(_.length == 3). // Catch under-full xs
zipWithIndex.
find{ case (x, _) => x(0) == n && x(2) == n) }.
map(_._2).getOrElse(-1)
Or I could write a while loop:
def fastButMaybeWrong(xs: Array[Int], n: Int): Int = {
var i = 2
while (i < xs.length) {
if (xs(i) == n && xs(i-2) == n) return i-2
i += 1
}
-1
}
But best of all would be
def recursiveSeek(xs: Array[Int], n: Int, private i: Int = 2): Int =
if (i >= xs.length) -1
else if (xs(i) == n && xs(i-2) == n) i-2
else recursiveSeek(xs, n, i+1)
However, I still don’t think the feature quite pulls its weight. It’s really cool, but Scala has too many cool things to remember already. I wouldn’t be sorry to see it go in, and I’d use it extensively, but in the full cost-benefit analysis I can’t make a strong case that it’s justified.