Search code examples
scalascala-streams

How does the lazy 'take' function compute the Scala stream further?


In Martin Odersky’s book “Programming in Scala” there is an example of computing the Fibonacci sequence starting with 2 numbers passed as arguments to the function fibFrom.

def fibFrom(a: Int, b: Int): Stream[Int] =
       a #:: fibFrom(b, a + b)

If you apply the method take() to this recursive function like:

fibFrom(1, 1).take(15).print

The output will be:

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, empty

Maybe this output is obvious for more experienced people, but I don’t understand how exactly this method take() makes the stream to be calculated further. Is 15 somehow nonobviously passed into fibFrom()?


Solution

  • With

    a #:: fibFrom(b, a + b)
    

    you created Stream object, and that object has head which is Int and tail which is function. Take is function of Stream that will calculate 15 elements using tail and head. You can check source code of take() function:

      override def take(n: Int): Stream[A] = (
        // Note that the n == 1 condition appears redundant but is not.
        // It prevents "tail" from being referenced (and its head being evaluated)
        // when obtaining the last element of the result. Such are the challenges
        // of working with a lazy-but-not-really sequence.
        if (n <= 0 || isEmpty) Stream.empty
        else if (n == 1) cons(head, Stream.empty)
        else cons(head, tail take n-1)
      )