I've been working with Scala a bit, just to learn what it is. I've found it interesting, if frustrating. Here is a bowling experiment.

The bowling problem that Chet and I use for TDD demonstrations is somewhat different from the one that Bob Martin uses. Ours can be stated: “Given a list of rolls of a legal game of bowling, compute the final score of the game.” This turns out to be just big enough to be interesting and to be something we can do in about an hour, with plenty of discussion and questions.

I wrote this one in full TDD style, but I will present here only the final version, with some discussion. Mostly I spent the early part of the project trying to learn the Scala language and tools, and trying to work in what I hope is a Scala-like style. I’ll leave the article open for comments in case you’d like to advise me otherwise.

First, the Tests

Here are the tests, written in ScalaTest Spec format. I kind of like that form, as it means I don’t have to make up really weird test method names.

import org.scalatest.Spec

class ExampleSpec extends Spec {

  describe("A Bowling Game") {

    it("should score all gutters as zero") {
    	val game = new Game(List(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
    	expect(0)(game.score)
    }

    it("should score all 5,4 as 90") {
    	val game = new Game(List(5,4,5,4,5,4,5,4,5,4,5,4,5,4,5,4,5,4,5,4))
    	expect(90) (game.score)
    }

    it("should score spare game 6, 4, 5, 2 as 22") {
    	val game = new Game(List(6,4,5,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
    	expect(22)(game.score)
    }

    it("should score strike game 10, 5, 2, 7 as 31") {
    	val game = new Game(List(10,5,2,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
    	expect(31)(game.score)
    }

    it("should score perfect game as 300" ) {
    	val game = new Game(List(10,10,10,10,10,10,10,10,10,10,10,10))
    	expect(300)(game.score)
    }
  }
}

</code>

As always, these are shown in the order created.  Nothing special here, I just create a game and expect its score. I did, of course, work one test at a time, making each one work before creating a new one.

I had a design in mind, as always. I think it is neither desirable, nor quite likely even possible, to work with no design in mind. The trick is to make no steps in the direction of the design until the code calls for them. You’ll have to take my word for it that I accomplished that. The point here is just to show you the result, which I think is interesting.

The design I had in mind is one that Chet and I tried in Java or C# last week. Under the Game object, there is a “Framer” object that steps along the sequence of rolls, positioning itself to the start of each frame, and adding the score of that frame to the game total. In Java, we wound up having that object maintain an index into the array of rolls, and it would step that index by 2 for spares and open frames, and by one for strikes. (All done one step at a time, of course.)

Now, the Game

The game didn’t change after I first refactored in a rudimentary framer. It goes like this:

class Game(rolls: List[Int]) {
   def score = {
      val framer = new Framer(rolls)
      var total = 0
      for (frame < - 1 to 10) { total += framer.score }
      total
   }
}

</code>

Not much to see here. As I look at it now, I suspect there is a more Scala-like way  to do this. If you see one, please let me know.

And, the Framer

The Framer class has gone through a number of iterations, from a procedural, indexed approach, to this more functional one. One essential idea is that this Framer, rather than indexing through the list of rolls, actually consumes it. The other idea is that at each frame position, the Framer adds together two rolls, or three rolls, to get the frame score. An open frame scores just the two rolls of the frame, while a strike scores the one roll of the frame and the next two rolls, while a spare scores the two rolls of the frame and the next one. Since 2 + 1 equals 1 + 2, both “mark” frames wind up scoring three rolls.

I first had the idea of creating a tuple saying how many rolls to score, and how many to consume or “eat”. Then I hit on using Scala pattern matching to identify which kind of frame we’re dealing with.

Here again, I’m open to comments on style, and offers of better ways of doing it.

class Framer(var rolls: List[Int]) {
   def score = scoreFrame(frameList)

   def scoreFrame(sumAndEat: Tuple2[Int,Int]) = {
      val rollsToSum = rolls.take(sumAndEat._1)
      rolls = rolls.drop(sumAndEat._2)
      rollsToSum reduceLeft (_+_)
   }   

   def frameList = {
      rolls match {
         case List(10, _*) => (3,1)
         case List(a:Int, b:Int, _*) if (a + b == 10) => (3, 2)
         case _ => (2,2)
      }
   }
}

</code>

Overall, it was a somewhat frustrating experience, but mostly this was due to trying to hook together all the tools one would need. Partly, however, I found the two books I had, Wampler’s and Venkat’s, didn’t lend themselves to my style of learning, whatever that is.

Some of that is due to the fact that I am a Windows user and prefer to use an IDE. I realize this calls my masculinity into question, but I can live with that.

Anyway, that’s my report. I look forward to your comments, and I’ll probably write a few more articles about my experience with Scala. Enjoy!


Updates from Ilmari

Based on Ilmari’s suggestions, we can rewrite Framer as:

class Framer(var rolls: List[Int]) {
   def score = scoreFrame(frameList)

   def scoreFrame(sumAndEat: (Int,Int)) = {
      val rollsToSum = rolls.take(sumAndEat._1)
      rolls = rolls.drop(sumAndEat._2)
      rollsToSum reduceLeft (_+_)
   }   

   def frameList = {
      rolls match {
         case 10 :: theRest => (3,1)
         case first :: second :: theRest if (first + second == 10) => (3, 2)
         case _ => (2,2)
      }
   }
}

</code>

Frankly, I’m surprised by the match … apparently everything inside a case is taken implicitly as abstract parameters. Very interesting.

I couldn’t find a way to get rid of the ._1 and ._2 in the scoreFrame method: everything I tried to give those two Ints names wouldn’t compile.

As for Ilmari’s comment about the 1 to 10, I don’t see how to get rid of that either. Would like to learn more …


Another Pattern Match

I fiddled with the pattern match a bit, making it return two lists, the rolls to sum, and the rolls to work with next time. You’ll notice I’m still referring to the rolls state variable and storing over it. The scoreFrame method is simpler now: I’m not so happy with the pattern itself. Your thoughts?

<pre> class Framer(var rolls: List[Int]) { def score = scoreFrame(frameList)

def scoreFrame(sumAndEat: (List[Int], List[Int])) = { rolls = sumAndEat.2 sumAndEat._1.reduceLeft (+_) }

def frameList = { rolls match { case 10 :: second :: third :: theRest => (10 :: second :: third :: Nil, second::third::theRest)

     case first :: second :: third :: theRest if (first + second == 10) =&gt;
     	(first::second::third::Nil, third::theRest)
     	
     case first::second::theRest =&gt; (first::second::Nil, theRest)
     
     case Nil =&gt; (Nil, Nil)
  }    } }</pre></code>