Private Parameters

#1

So it would be nice if a method like:

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.

1 Like
#2

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?)

1 Like
#3

I’ve accomplished this before with overloading and a private recursive helper. Perhaps that would desugar to something like the following?

def factorial(n: Int): Int = factorial(n, acc = 1)

private def factorial(n: Int, acc: Int): Int = if (n > 0) factorial(n -1, n * acc) else acc

I like the idea, but overloading the keyword private is problematic, because it doesn’t mean the same thing in constructors.

4 Likes
#4

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.

#5

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.

4 Likes
#6

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.

1 Like