Scala Bowling Kata - somewhere in the middle... 44
- Installed the Eclipse Scala Plugin
- Installed Scala using Mac Ports
- Figured out how to get those things playing nice (the plugin page pretty much did that, but in a nutshell, add a few jar files to the classpath)
- We don’t need YADT – Yet another damn term
- Trait is a heavily overloaded word
- I like the term BDD better and it fits.
Anyway, one such choice was Specs, which is what I decided to use.
So back to yak shaving:- I added another jar to my classpath in Eclipse
- Then read how to get it running in Eclipse. Not too bad, I suppose.
So now I need to learn Scala. Sure, I’ve used it, but far less than Ruby. So it took me several hours to get specs running along with writing some Scala code to score a game – I’m glad I know the domain at least.
I wanted to make similar behaviors to the ones I wrote for the Ruby version, which I did.
However, unlike the Ruby version, I was curious what would happen if I:- Took an approach similar to Uncle Bob – strikes take one slot in an array
- Added input validation
On the one hand, there are some interesting things I managed to create. On the other hand, I’ve got a bit of a mess. I have a stateful object to avoid passing parameters so that I can write some of the code cleanly. I know I need to add in an intermediate computational object, and I’m going to get to that. However, I wanted to get feedback on what I’ve put out there so far.
Specifically,- What do you think of the (bdd-style) examples from specc?
- What is the correct way to write the Times(20).Do( ...) thing I came up with, there has be a better way?
- For the part of the bowling scoring code that is not stateful (read this as, does not violate the SRP), what do you think of it?
- How would you remove most/all of the state (other than the individual rolls) out of the Bowling scorer class? (Or would you choose to have the roll() method return a new instance of BowlingScorer with the new score recorded?)
- Notice that the class maintains a mini state machine in the form of tracking whether the first ball of he current frame (not tracked) has or has not been thrown. That’s only there to be able to perform input validation. I considered:
- Walking the array
- Going to 2 slots for every frame (making it easy to find the frame)
- Storing a frame object (ok, I didn’t really consider it, but I did think about it)
- The mini state machine
- nextFrameScore uses the index instance variable, and changes it. This both violates command-query separation and demonstrates a violation of the SRP, but it made the scoreAt method look nice.
An interesting side effect is that scoring marks (strikes and spares) uses the same approach, sum up three rolls total.
I know this needs work. What I’ve got works according to its current specification (its examples), so in a sense, that’s a good thing because I’ve already stared experimenting with trying out different solutions. However, I am painfully aware of how unaware I am of Scala at the moment, so your (hopefully gentle) feedback will tell me what I need to learn next.
Looking forward to the virtual beating …
Brett
Here are the two files I’ve created so far (and to be clear, all of the examples pass): BowlingScorerExampleGroup.scalapackage com.om.example
import org.specs._
object BowlingScorerExampleGroup extends SpecificationWithJUnit {
var scorer = new BowlingScorer();
def roll(value:Int) {
scorer.roll(value)
}
def rollMany(rolls:Int, value:Int) {
0.until(rolls).foreach { arg => scorer.roll(value) }
}
def haveAScoreOf(expected:Int) {
scorer.score must_== expected
}
def strike {
roll(10)
}
def spare {
rollMany(2, 5)
}
abstract class IDo {
def Do(block: => Unit)
}
def Times(count:Int): IDo = {
return new IDo {
def Do(block: => Unit) {
1.to(count).foreach( arg => block )
}
}
}
"A Newly Created Bowling Scorer" should {
haveAScoreOf(0)
}
"A game with all 0's" should {
Times(20).Do( roll(0) )
haveAScoreOf(0)
}
"A game with all 1's" should {
Times(20).Do { roll(1) }
haveAScoreOf(20)
}
"A game with a single spare followed by a 5" should {
spare
roll(5)
haveAScoreOf(20)
}
"A game with all 5's" should {
Times(10).Do( spare )
roll(5)
haveAScoreOf(150)
}
"A game with a single strike followed by a 4" should {
strike
roll(4)
haveAScoreOf(18)
}
"A game with a strike, spare then an open frame with two 3's" should {
strike
spare
Times(2).Do( roll(3) )
haveAScoreOf(39)
}
"A game with strike, spare then an open frame with two 3's" should {
spare
strike
Times(2).Do( roll(3) )
haveAScoreOf(42)
}
"A Dutch 200 game, Spare-Strike" should {
Times(5).Do {
spare
strike
}
spare
haveAScoreOf(200)
}
"A Dutch 200 game, Strike-Spare" should {
Times(5).Do {
strike
spare
}
strike
haveAScoreOf(200)
}
"A Perfect game" should {
Times(12).Do( strike )
haveAScoreOf(300)
}
"The score for each frame of a Perfect game, each frame" should {
Times(12).Do( strike )
1.to(10).foreach{ frame => scorer.scoreAt(frame) must_== 30 * frame }
}
"An individaul roll of > 10" should {
roll(11) must throwA[IllegalArgumentException]
}
"An iniviaul roll of < 0" should {
roll(-1) must throwA[IllegalArgumentException]
}
"A frame trying to contain more than 10 pins" should {
roll(8)
roll(3) must throwA[IllegalArgumentException]
}
}
BowlingScorer.scala
package com.om.example
class BowlingScorer {
var rolls:Array[Int] = Array()
var index:Int = 0
var firstBallInFrameThrown: Boolean = false;
def roll(roll:Int) = {
validate(roll)
record(roll)
}
def validate(roll:Int) {
if((0).to(10).contains(roll) == false)
throw new IllegalArgumentException("Individaul rolls must be from 0 .. 10")
if(openScoreAt(indexToValidate) + roll > 10)
throw new IllegalArgumentException("Total of rolls for frame must not exceed 10");
}
def record(roll: Int) {
rolls = rolls ++ Array(roll)
firstBallInFrameThrown = firstBallInFrameThrown == false && roll != 10
}
def indexToValidate = {
if(firstBallInFrameThrown) rolls.length - 1 else rolls.length
}
def scoreAt(frame:Int) = {
1.to(frame).foldLeft(0) { (total, frame) => total + nextFrameScore }
}
def score = {
scoreAt(10)
}
def nextFrameScore = {
var result = 0;
if(isStrike(index)) {
result += markScoreAt(index)
index += 1
} else if(isSpare(index)) {
result += markScoreAt(index);
index += 2
} else {
result += openScoreAt(index);
index += 2
}
result
}
def isStrike(index:Int) = {
valueAt(index) == 10
}
def markScoreAt(index:Int) = {
sumNext(index, 3)
}
def isSpare(index:Int) = {
openScoreAt(index) == 10
}
def openScoreAt(index:Int) = {
sumNext(index, 2)
}
def sumNext(index:Int, count:Int) = {
index.until(index+count).foldLeft(0)(_ + valueAt(_))
}
def valueAt(index:Int) = {
if(rolls.length > index) rolls.apply(index) else 0
}
}
I have done the same Kata in Scala here: http://gist.github.com/188452
I feel my version looks less ‘java’ compared with this one.
BowlingScorer looks like a good fit for an immutable case class containing a List (adding a new roll to the list is constant time and folding over the list is quite fast), ie:
case class BowlingScorer(rolls : List[Int]) { /* Method implementations left as an excercise */ }
My first advice would be to avoid being so Lisp-ish… drop the parenthesis! And the dots, of course.
I feel that operator style notation reads more clearly.
As for “Times”... one could write a book on variations thereof.
For instance, as an implicit on a function:
implicit def toTimes(f: Function0[Unit]) = new AnyRef { def times(n: Int) = 1 to n foreach (_ => f()) }
Used like this:
(() => println(“Hello”)) times 5
Or as an implicit on Integer:
implicit def toTimes(n: Int) = new AnyRef { def times(f: => Unit) = 1 to n foreach (_ => f) } 5 times println(“Hello”)
As a curried function:
def times(n: Int)(f: => Unit) = 1 to n foreach (_ => f) times (5) (println(“Hello”))
Now, this is not faster implementation you could have. For that, “while” is unbeatable.
The thing is, “times”, as simply repeating something that returns nothing, is pretty useless… unless you are benchmarking, or using side effects! So, once you get rid of doing things in a side effectfull manner, what would be the choices?
Here is one possibility:
def rollMany(rolls: Int, value: Int, s: Score) = List.fill(rolls)(value).foldLeft(s) { (score, value) => score roll value } def spare(s: Score): Score = rollMany(2, 5, s)
Not very composable, though. Better to pass the function:
def rollMany(rolls: Int, f: Score => Score) = (s: Score) => (1 to rolls foldLeft s) { (score, _) => f(score) } val spare = rollMany(2, _ roll 5) val strike = (_: Score) roll 10
You can then compose:
val dutch = rollMany(5, strike andThen spare) andThen strike
You can then test it by calling it with an initial Score:
dutch(InitialScore)
At any rate, these are just a few ideas to prod you into direction you might not have considered.
An addition worth considering for a Concurrency for Java class is Jetlang (http://code.google.com/p/jetlang/). Jetlang is a message based concurrency Java library which virtually eliminates the need for locks.
One of the nicest features is that it makes testing very easy via the FiberStub, providing an example of how to nicely abstract away threading as suggested in the concurrency chapter of Clean Code.
The author, Mike Rettig, even partially integrated Jetlang into Scala (http://www.jroller.com/mrettig/).
I still don’t understand the attraction of BDD. When I see the output of BDD, it’s indistinguishable from what from my POV is just a good Test. In fact I’d call what I/we do much closer to hypothesis-testing, whether that’s about behavior (as it mostly is) or state (as it sometimes is).
I guess it’s a powerful enough shift in the minds of BDD’ers that they don’t mind pulling the rug out from under testing conversations. Am I supposed to say “test” or “spec”, “BDD” or “TDD”. Am I going to run my testsuite and see if I’m green? Oops wrong word.
Giulio Cesare Solaroli,
I had a look at your code on git hub. I like the better use of list expressions. Initially I did not like the verbosity of your tests, but upon reflection, it makes the rolls more clear, so I do like it.
However, you’ve done the easy part of the kata. Your code does no error checking, so until you’ve done that, your code will be incomplete. And it’s the error checking that makes the code uglier.
In fact, having gone through this, I’m going to generally change my BDD approach to introduce error checking sooner. In the past I was strictly mostly happy path and then edge cases. Now I’m reconsidering that approach.
Jesper Nordenberg,
Agreed. All of the state bothered me. However, I had a 3.5 hour flight so with all of my tests passing, I started slowly removing it. And I managed to get rid of all state. The bowling scorer is immutable. The roll method returns a new instance of a bowling scorer with an updated list of scores.
I’ll be posting it soon. I still have one (or 2?) ugly methods.
Daniel Sobral,
Thanks for all of the tips! I did some of what you recommended but I’ve got more to go. I’ll be posting an update with a few more questions.
Also used a foldLeft (I think of this as inject:into:) with a tuple to remove some of the state…
the pposr os really good
These databases are typically managed with functional techniques, like map-reduce.
The christmas time is comeing, white iphone 4 conversion kit will be the best present for yourself and family.
Mold making is the core business of Intertech (Taiwan). With world level technology, Intertech enjoys a very good reputation for making Injection Mold and Plastic Moldsfor their worldwide customers.
The benefits of print on demand
Watercan, Leaders Of Clean Water Projects In Developing Countries
Thanks for shareing! I agree with you. The artical improve me so much! I will come here frequently. Would you like to banckup iphone SMS to mac, macBook, macbookPro as .txt files? Now a software iphone SMS to Mac Backup can help you to realize it.
I really like this essay. Thank you for writing it so seriously. I want to recommend it for my friends strongly. iPad to Mac Transfer can help you transfer music, movie, photo, ePub, PDF, Audiobook, Podcast and TV Show from ipad to mac freely.
Your site is amazing.I am very impressed to see this,i want to come back for visiting your site.Keep doing Good as well as you can.
KEESTRACK?manufacturing expert of mobile crusher and mobile screening equipment. Company engaged in the research & development, manufacture, sales of track screening machine,mobile screening,cone crusher,jaw crusher,impact crusher,mobile screening equipment,mobile crushing equipment, till now we are proud to say we are at front of the industry.
The new installation is very must complicated It should be more clear. daycare grants
In place of a wheel train to add up the beats into seconds, minutes, and hours, it used kids sports watchdigital counters. The higher Q factor of the resonator, along with quartz’s low temperature coefficient, resulted in better accuracy than the best mechanical watches, while the elimination of all moving parts made the watch more shock-resistant and eliminated the need sport chronograph watchesfor periodic cleaning.
Amazing! This is very informative blog. Thanks for sharing such a nice information with us. I appreciate your blogdaycare job
I got these guides very helpful. Here I got much stuff to know..Foreclosure Help in Alaska
Thank You for this wonderful post!Ohio Daycare Grants
yes it is very nice.
internette görüntülü olarak okey oyunu oyna, gerçek kisilerle tanis, turnuva heyecanini yasa.
Nice posts! thumbs up!New Hampshire Daycare Form
Mr Coates coach purses is the longest U.S. market popular with one of the most successful leather brand. Mr Coates coach purses store represents the most admirable American fashion innovative style and traditional skills . Mr Coates coach bags have durable quality and exquisite technology, Conspicuous Coach Heels in the female consumers have good reputation. Welcome to our shop Elegant Coach Purses
Good effort! keep up the good work! wypall x60
I got these guides very helpful. Here I got much stuff to know..absorbant
I got these guides very helpful. Here I got much stuff to know.. Illinois Foreclosure Help
I got these guides very helpful. Here I got much stuff to know..Daycare Grants in South Dakota
Would you like to banckup iphone SMS to mac, macBook, macbookPro as .txt files? Now a software iphone SMS tohigh quality headphones new design headphones
It is nice of you to post this.I will pay more attention on it. Discount fashion women Prada Lowcut shoes from China for wholesale free shipping,more order,more discount
To read a worth knowing article is so overwhelming.
asdasd +9f8as9+d9fs
asdfs fgh98f7g88f8f8f
asa sdf+09a8s0+ 90s8a9sss
Berkeley had a liberal element in the student body who tended to be quite active. I think that’s in general a feature of intellectually active places.
Berkeley had a liberal element in the student body who tended to be quite active. I think that’s in general a feature of intellectually active places.
As some conservative Republicans sought an alternative to Mitt Romney Supra Pilot Shoes
You still have an extremely beneficial weblog I have already been here looking at for about quite some time currently. I am a newcomer plus your achievement is extremely significantly a great inspiration personally. Carry on the nice article!
It is so hard to understand this. can u explain more about this topic.
Many thanks regarding sharing us all about this revise. We imagine you will not find fatigued about generating posts as useful simply because this.
Scala Bowling Kata – somewhere in the middle… 41 good post36
Scala Bowling Kata – somewhere in the middle… 42 hoo,good article!!I like the post!177
Which is a number of motivational things. Never knew that ideas could be this particular different.