2

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)
    }
}
Community
  • 1
  • 1
smac89
  • 39,374
  • 15
  • 132
  • 179
  • What version of Scala? Does the `java.lang.StackOverflowError` pop up on definition or when you try to access a member of the stream? Both versions work fine for me on Scala 2.11 – evan.oman Feb 07 '17 at 17:11
  • 1
    @evan058, My scala version is 2.11.6, and the java version is 1.8.0_121 – smac89 Feb 07 '17 at 17:13
  • @evan058 I can reproduce the error on 2.12.1. I don't see why it wouldn't happen on 2.11. Did you actually use the list or just define it? – sepp2k Feb 07 '17 at 17:13
  • I used it, I can get the 10,000th prime from each no problem – evan.oman Feb 07 '17 at 17:15
  • 1
    @sepp2k Weird, [here is the screenshot of my Jupyter Notebook](http://imgur.com/a/tmvZe). Am I missing something? – evan.oman Feb 07 '17 at 17:22
  • 3
    @evan058 In your code (as well as in the code in the OP's edit, which I assume is a typo/copy-and-paste error) `primes2` uses `primes` instead of using itself. In the original code the stream is called `primes` and uses itself. That version will cause the stack overflow. – sepp2k Feb 07 '17 at 17:37
  • @sepp2k Ha, there we go, thanks for clearing that up! – evan.oman Feb 07 '17 at 17:50

2 Answers2

8

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.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • Thanks for the explanation. Do you have a reference to a document or link that explains this in more detail? – smac89 Feb 07 '17 at 17:36
  • 2
    @smac89 http://www.scala-lang.org/docu/files/ScalaReference.pdf section 6.12, on page 84. 6.12.3 specifically talks about infix operators and precedence – puhlen Feb 07 '17 at 18:53
2

You need parenthesis:

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

As a rule of thumb, I usually use dots everywhere. It's easier to read and makes these sort of bugs harder to appear.

nmat
  • 7,430
  • 6
  • 30
  • 43
  • Lol you're right about the dots. I tend to get lost in use of no dots because it make the coding look more natural, almost like typing a sentence. – smac89 Feb 07 '17 at 17:23