Coding in Scala while reading »Thinking Functionally with Haskell«

While reading Thinking Functionally with Haskell I coded the examples in Scala. Without further ado:

scala> lazy val fizz: Stream[String] = "" #:: "" #:: "Fizz" #:: fizz
three: Stream[String]

scala> lazy val buzz: Stream[String] = "" #:: "" #:: "" #:: "" #:: "Buzz" #:: buzz
five: Stream[String]

scala> fizz zip buzz foreach println
(,)
(,)
(Fizz,)
(,)
(,Buzz)
(Fizz,)

// She’s gone rogue, captain! Have to take her out!
// Calling Thread.stop on runaway Thread[Thread-9,5,main] with offending code:
// scala> fizz zip buzz foreach println

Note that it takes some time until you see the effect of pressing Ctrl-C. Being impatient and pressing Ctrl-C twice will exit the Scala REPL.

Adding an natural number index and some nice formatting:

scala> fizz zip buzz zip Stream.from(1) map {case ((a,b), n) => "%s: %s%s".format(n, a, b)} foreach println
1:
2:
3: Fizz
4:
5: Buzz
6: Fizz
7:
8:
9: Fizz
10: Buzz
11:
12: Fizz
13:
14:
15: FizzBuzz

Now adding some filtering to see just the FizzBuzz lines:

scala> val stream = fizz zip buzz zip Stream.from(1) filter {case ((a,b), n) => a=="Fizz" && b=="Buzz"}
stream: scala.collection.immutable.Stream[((String, String), Int)] = Stream(((Fizz,Buzz),15), ?)

scala> stream take 3 foreach println
((Fizz,Buzz),15)
((Fizz,Buzz),30)
((Fizz,Buzz),45)

The Haskell examples are using zipWith which is not available in Scala (see SI-1512). To follow the examples I defined my own zipWith function which assumes that the lists are of the same size:

scala> def zipWith[A, B, C](f: (A, B) => C, s1: List[A], s2: List[B]):List[C] = s1 match {
case Nil => Nil
case _ => f(s1.head, s2.head) :: zipWith(f, s1.tail, s2.tail)
}
zipWith: [A, B, C](f: (A, B) => C, s1: List[A], s2: List[B])List[C]

scala> def max(a:Int,b:Int):Int = if (a>b) a else b
max: (a: Int, b: Int)Int

scala> zipWith(max, List(1,2,3), List(2,2,2))
res9: List[Int] = List(2, 2, 3)

scala> def *(a:Int, b:Int):Int = a*b
$times: (a: Int, b: Int)Int

scala> zipWith(*, List(1,2,3), List(2,2,2))
res10: List[Int] = List(2, 4, 6)

I also defined a zipWith function which works with Streams:

scala> def zipWith[A, B, C](f: (A, B) => C, s1: Stream[A], s2: Stream[B]):Stream[C] = f(s1.head, s2.head) #:: zipWith(f, s1.tail, s2.tail)
zipWith: [A, B, C](f: (A, B) => C, s1: Stream[A], s2: Stream[B])Stream[C]

scala> zipWith(concat, threes, fives) foreach println

The lazy evaluation streams provide is really a powerful concept!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s