0

I know while using Loops, mutability comes into picture and keep things difficult to have a track of. But is it just because of mutability that recursion is considered over loops in Scala?

Additionally I understand that tail recursion doesn't add to your call stack but not all the problems can be solved using tail Recursion right? What about using an Accumulator based approach which also seems to be sufficient enough for avoiding stack overflow cases? What can be more performant between tail Recursion and Accumulator based Recursion Approach?

In short, I have two questions:

  1. Since tail recursion cannot be used to solve all loop problems (or can it?) isn't looping the better or the only solution for some specific use cases?
  2. What is the difference between tail recursion and accumulator based recursion and which is more performant based on the space complexity and call stack occupied?

Edit 1:

Example of a non tail recursive function (which uses an intermediate tail recursive function) that is converted in Accumulator based recursion (Factorial Function):

Without Accumulator:

def factorial(n: Int): Int = {
  if (n <= 1) 1
  else n * factorial(n - 1)
}

With Accumulator:

def tailRecFactorial(n: Int): BigInt = {
    @tailrec
    def factorialHelper(x: Int, accumulator: BigInt): BigInt = {
      if (x <= 1) accumulator
      else factorialHelper(x - 1, x * accumulator)
    }
    factorialHelper(n, 1)
  }
Shivam Sahil
  • 4,055
  • 3
  • 31
  • 62
  • 1
    Can you give an example of what you see as accumulator-based recursion? – Levi Ramsey Nov 26 '21 at 14:07
  • 1
    Note that it's not that uncommon to refer to some of the arguments in a tail-recursive function to be referred to as "accumulators". – Levi Ramsey Nov 26 '21 at 14:26
  • 2
    "Since tail recursion cannot be used to solve all loop problems (or can it?)" – Tail recursion *can* be used to solve all loop problems, and the proof is trivial: you can implement `while` tail-recursively in about two lines of Scala. `def myWhile(cond: () => Boolean)(body () => Unit) = if (cond()) { body(); myWhile(cond)(body) }` or something along those lines. – Jörg W Mittag Nov 26 '21 at 18:07
  • 2
    https://stackoverflow.com/questions/2185532/why-should-recursion-be-preferred-over-iteration https://stackoverflow.com/questions/478570/recursion-or-iteration https://stackoverflow.com/questions/72209/recursion-or-iteration – Dmytro Mitin Nov 27 '21 at 08:18
  • 1
    https://scastie.scala-lang.org/JoergWMittag/cWuP0UdxR5SGZoRK1VJUrg/3 – Jörg W Mittag Nov 27 '21 at 09:25

2 Answers2

2

Scala is a functional language, and functional code is generally considered better than non-functional code. The objection to while is that it relies on a value changing during the iteration and therefore cannot be functional.

To answer the specific questions:

  1. Any loop can be expressed as a pure tail recursive function, but it can get very hairy working out what state to pass to the recursive call.

  2. "tail recursion" and "accumulator based recursion" are not mutually exclusive. A tail recursive function is any function that calls itself as the last action on at least one of the code paths. Accumulator based recursion simply means passing a partial result to the recursive call as a way of converting a non-tail-recursive function to a tail recursive function.

Tim
  • 26,753
  • 2
  • 16
  • 29
1

Well, first of all, not sure what you mean by accumulator-based recursion?

Second, Scala only has while loop, and all while loops can be expressed in terms of (tail) recursion; AFAIK.

Third, yeah, we prefer recursion over while to avoid mutability.
But still, recursion itself is too low level. In general, you should prefer higher-order combinators like map or foldLeft

Fourth, while mutability is contained in a single method is OK, is usually better to be consistent everywhere and keep everything immutable (unless you have very good reasons for the otherwise) than randomly mix mutability with immutability just because.

  • I very much disagree that recursion is "too low level". Recursion is a technique that every functional programmer should have in their toolbox and use when library methods are not flexible enough. I have seen plenty of answers here with complex and opaque `fold` algorithms where a recursive function is simpler and clearer. – Tim Nov 26 '21 at 15:03
  • 1
    @Tim sure, totally agree it is a technique everyone should know and that, as with everything, sometimes is the best solution; that is why I said _"you should prefer"_ not _"you must always use"_. Still, that doesn't deny the fact that recursion is lower level than higher-order combinators. - Also, you know I actually like tail recursion a lot and many of my answers here use tail recursion, answer that BTW had been criticized by using tail-recursion instead of `foldLeft` – Luis Miguel Mejía Suárez Nov 26 '21 at 15:11
  • 1
    Yes, that remark was not aimed at you :) I guess my thinking is that recursion is an abstract technique that is applicable to a wide range of problems whereas higher order combinations are more focussed and limited in scope. But perhaps high level vs low level is just not a useful way of comparing the two. – Tim Nov 26 '21 at 15:24