Search code examples
scalastack-overflowprimesoperator-precedencelazy-sequences

Scala function call without "." (dot) vs using "." (dot)


Can someone help me understand what is going on here. I have this definition for generating primes:

def primes: Stream[Long] = {
    2 #:: 3 #:: 5 #:: 7 #::Stream.iterate(11L)(_ + 2).filter {
        n => primes takeWhile (p => p*p <= n) forall (n % _ != 0)
    }
}

def primes: Stream[Long] = {
    2 #:: 3 #:: 5 #:: 7 #::Stream.iterate(11L)(_ + 2) filter {
        n => primes takeWhile (p => p*p <= n) forall (n % _ != 0)
    }
}

As you can see, both definitions are exactly similar except for the fact that the second one does not have a . before filter, whereas the first one does.

The problem is that running the first one, runs as expected and gives us primes, but the second one produces a java.lang.StackOverflowError. Can someone shed some light on this? What is being passed to filter in either case?

Scala version: 2.11.6

Java version: 1.8.0_121

This is the full program I used to test each one:

object Main {

    def primes: Stream[Long] = {
        2 #:: 3 #:: 5 #:: 7 #::Stream.iterate(11L)(_ + 2) filter {
            n => primes takeWhile (_ <= sqrt(n)) forall (n % _ != 0)
        }
    }

    def primes2: Stream[Long] = {
        2 #:: 3 #:: 5 #:: 7 #::Stream.iterate(11L)(_ + 2).filter {
            n => primes2 takeWhile (p => p*p <= n) forall (n % _ != 0)
        }
    }

    def main(args: Array[String]): Unit = {
        println(primes.take(args.head.toInt).force)
    }
}

Solution

  • The notation without . has the same precedence as that of any custom infix. So the first one applies filter only to Stream.iterate(11L)(_ + 2) - the second one applies it to 2 #:: 3 #:: 5 #:: 7 #::Stream.iterate(11L)(_ + 2).

    The reason why the first one works is that the elements 2, 3, 5 and 7 are already in primes when the filter runs, so when the filter tries to use primes, those elements are already in it.

    In the second code that's not the case because the filter is applied to those elements as well, meaning they wouldn't appear in primes until the filter returned true for them. But the filter needs to get elements from prime before it can return anything, so it loses itself in infinite recursion while trying to get at an element.