Streams in Scala: Part 3

If you’ve been reading along in this series on streams, we’ve covered a quick introduction to streams in part one and some basic stream library methods in part two.

Elegance in streams is an issue I have run into quite often as I’ve been learning Scala. How do I make my intent completely clear? Is it possible to declare a stream in language very close to English and make it well performant? Can we even achieve one of those things? Can we only achieve one but not the other?

The question arises because streams are so flexible. There are a lot of ways to define the same stream. You can use the library methods. You can use a recursive function. You can use a tail-recursive function.

Consider the squares example that has come up in almost every one of my posts on Scala so far. Here is a version of a stream of square numbers declared from a recursive function.

scala> def squares(x: Int): Stream[Int] = {
| Stream.cons(x * x, squares(x + 1));
| }
squares: (x: Int)Stream[Int]

And here is a version using the Stream library.

scala> def squares = Stream.from(1) map { x => x * x }
squares: scala.collection.immutable.Stream[Int]

And you could also do this, but why would you?

scala> def squares = Stream.from(1) zip Stream.from(1) map { n => n._1 * n._2 }
squares: scala.collection.immutable.Stream[Int]

Which of these is more clear? I tend to prefer the syntax of the second example (Stream.from(1) map { x => x * x }). It almost reads like English: “Define squares as a stream from one mapping each value to a function that returns the result of that value multiplied by itself.”

But this might be even better:

scala> def squareOf(x: Int) {
| x * x
| }
squareOf: (x: Int)Unit

scala> def squares = Stream from 1 map { squareOf(_) }
squares: scala.collection.immutable.Stream[Unit]

Now it reads like this: “define squares as a stream from 1 mapped to the square of each value.”

Using an implicit conversion we can get this to read even clearer:

import scala.language.implicitConversions

class IntMethods(x: Int) {
def squared = x * x

implicit def IntWrapper(x: Int) = new IntMethods(x: Int)

def squareNumbers = Stream from 1 map { _ squared }

Now it says, “define square numbers as a stream from 1 consisting of each value squared.” Changing the variable name to “squareNumbers” helps us to understand what exactly we are talking about. The old variable name “squares” could apply to several things: shapes, the carpenter’s tool, square numbers.

Elegance is not always so easy to define. For example, this is, in my opinion, a very elegant way to define the fibonacci series:

def fibonacciSeries: Stream[Int] = 0 #:: fibonacciSeries.scanLeft(1)(_+_)

Nevertheless, it doesn’t tell us very clearly what the fibonacci series is if we aren’t familiar with it already. It may be better to actually define this one as a recursive function, because that’s the way it is mathematically defined:

def fibonacci(x: Int): Int = {
  if(x < 2)     1   else     fibonacci(x - 2) + fibonacci(x - 1) } def fibonacciSeries: Stream[Int] = Stream.from(0) map { fibonacci(_) }

The problem with this is it is not tail-recursive! So although we gained clarity we did not gain performance.

In the API documentation fibonacci is defined as:

val fibs: Stream[BigInt] = BigInt(0) #:: BigInt(1) #:: { n => n._1 + n._2 }

That is quite a functional way of saying it: "fibs is a stream starting with 0, 1 and then generating each additional value by zipping fibs with its tail and mapping the resulting tuples to the sum of each tuple's value."

That's terrible to read! On the other hand it is quite performant.

So it may seem to be a little silly, but let's change the "elegant" fibonacci solution above and be extra-extra-clear with what's going on in it:

def nextFibonacci(lastFibonacci: Int, fibonnaciBeforeLast: Int): Int = { lastFibonacci + fibonnaciBeforeLast }
def fibonacciSeries: Stream[Int] = 1 #:: fibonacciSeries.scanLeft(1)(nextFibonacci(_, _))

Why is this better? (Arguably better.) Well because we are pretty clear about what this series is. The next Fibonacci is the sum of the last Fibonacci and the Fibonacci before last. So now that that's out of the way, we define the Fibonacci series as a stream of integers starting with 1, whose next element is generated by scanning the stream to the left of the current position and applying the next Fibonacci function to that value and the one before it.

Generally, it seems like a good idea to define the function that generates a particular number in a series before defining the series itself because it gives you some English for clarity. Scala is already pretty expressive so why not use it?

That said, compare the following full example below with the Java 8 and Java 7 versions of the same thing from a previous post.

Pretty cool, eh?

Leave a Reply

Your email address will not be published. Required fields are marked *