0
votes

My use-case is more complicated but basically below is the simplified example of what I am trying to achieve (my original code is for akka-stream):

offset := 0

//pump data
def pump(): Unit = {
    elem := poller.getNumberFromOffset(offset)

    elem match {
        case null => doSomething()
        case Completed => doSomethingElse()
        case _ => 
            offset += 1
            //check if the element is matched with a pre-supplied selector/filter function
            if(filterFunc(elem)) {
                doSomething2()
            } else {
                //if the element doesn't match; increase offset and try again; can sleep for a while here               
                pump()
            }
    }
}

The problem is that pump() function might result in stack-overflow (because pump function is called over-and-over again for a specific condition).

I can write the function into a non-recursive version like the following:

offset := 0

//pump data
def pump(): Unit = {
    elem := poller.getNumberFromOffset(offset)
    while(elem != null && elem != Completed && !filterFunc(elem)) {
        offset += 1
        elem = poller.getNumberFromOffset(offset)
    }       

    elem match {
        case null => doSomething()
        case Completed => doSomethingElse()
        case _ =>           
            offset += 1
            doSomething2()
    }
}

However my use-case is much more complicated; so I would really like to use the recursion function if it's possible instead of converting the existing code to while/for loop.

My question is "should I do so" and is there any difference if I just simply put @tailrec annotation on the first example so that scala complier will treat pump as tail-recursion function (@tailrec def pump(): Unit = {}). In my case, the pump() function should be called separatedly / after fixed interval; so stack-frame is really not necessary here.

Thanks.

1
"is there any difference if I just simply put @tailrec annotation on the first example" – No. The @tailrec annotation makes no difference. Tail-recursive methods are always optimized. (More precisely: methods that satisfy the conditions laid out in the spec.) The only thing the @tailrec annotation does is generate a compiler error if the method is not tail-recursive. It does not change whether or not the method gets optimized. - Jörg W Mittag
I challenge your pattern matching on null. Instead, I would wrap your possible null value in Option#apply. Example: Option[String] { null } === None - Kevin Meredith

1 Answers

1
votes

If you put @tailrec and the compiler lets you run it, this implies your piece of code is eligible for tail recursion optimization. Internally scala compiler will convert the tail recursive code to a loop. I dont think a tail recursive function will cause a stackoverflow error.So your pump() function wont cause stackoverflow if it satisfies @tailrec.