This is the second article in a series, to read the whole series go here.
This week I’ve worked on learning Scala’s basic syntactic structures and how to write unit tests. To accomplish this I’ve implemented a solution to the fizzbuzz problem and written a couple of unit tests to demonstrate that it works.
For an overview of the fizzbuzz problem, go to wikipedia’s page.
Conditionals, Loops and Other Constructs
Defining Methods
object Fizzbuzz {
// Returns true if the given number is divisible by 3
def isfizz(num : Int) : Boolean = { num % 3 == 0 }
// Returns true if the given number is divisible by 5
def isbuzz(num : Int) : Boolean = { num % 5 == 0 }
The method definition syntax is in the format: def <method name>(<arg name> : <arg type>) : <result type> = <body>
. You can see here that there is no ‘return’ statement in Scala. The value of any given block is the last statement executed in that block. This can lead to some confusion if you have very large methods with a lot of complex branching. The obvious solution is not to have very large methods. I generally start breaking up my methods if they start getting to be more than 12 lines long.
Scala is a statically typed language, which means that all of the variable/argument types are set at compile time. Fortunately it also uses Hindley-Milner type inference which will often guess the intended type of an argument/variable. Because of this the above code can be simplified as:
object Fizzbuzz {
// Returns true if the given number is divisible by 3
def isfizz(num : Int) = num % 3 == 0
// Returns true if the given number is divisible by 5
def isbuzz(num : Int) = num % 5 == 0
As far as I’m able to tell, unlike in some systems that use Hindley-Milner, types are required when defining the arguments for a method. I don’t really view this as a disadvantage, when I’m coding I get hopelessly confused if I don’t have the intended type of my variables noted somewhere, there’s no reason not to have it in a way that the compiler can catch my mistakes.
You can also see that when there’s only one statement in the method there’s no need for the curly braces around it. This can lead to significantly cleaner looking code when you just have a one liner.
If Statements and Exploring Your Options
def fizzbuzzSingle(num : Int) : Option[String] = {
if(num < = 0) None
else if(isfizz(num) && isbuzz(num)) Some("fizzbuzz")
else if(isfizz(num)) Some("fizz")
else if(isbuzz(num)) Some("buzz")
else Some(num.toString())
}
If Statements
One of the ways the Scala team tried to make their language more accessible is to keep the syntax similar to Java where they didn't either simplify it or add functionality which needed new syntax. So anyone who is familiar with any of the Algol type programming languages will recognize the format of the conditional statements above.
Algebraic Data Types
What is less common is Scala's use of an algebraic type system. In short this means that there are types that wrap other types. In the above case the Option
type can wrap an arbitrary types. I've used it to wrap a string, bit it could just as easily wrap an Int or a Window object. There are a myriad of reasons for using an algebraic type system. Here it allows me to indicate that an input is invalid (values less than 1) without throwing an exception.
When you are using a package that communicates failure through exceptions, it's often hard to know what exceptions to expect. By using the type system, if you know the type signature of your function/method, you can be sure what to expect as output from your function. When an invalid value is handed to a function using the Option type, it returns None
. When it receives a valid value then it returns Some(<response>)
.
Strictly speaking Scala doesn't have Algebraic types, here for example None
and Some
are subclasses of the abstract class Option
. But for practical purposes it's hard to tell the difference.
Comprehensions and Match
def fizzbuzzList(end : Int) : List[String] = {
for (num < - List.range(1, end + 1)) yield fizzbuzzSingle(num) match {
case Some(n) => n
case None => ""
}
}
For Comprehensions
For comprehensions are similar to list comprehensions in other languages with the syntax: for <value> <- <list> if <boolean statement> yield <result>
. This will go through each of the values in <list>
and if the <boolean statement>
returns true then yields the given <result>
. The if <boolean statement>
part is optional as in the above code.
Match/Case Statements
Scala's match/case statements are a generalization of the more common switch statement. The difference between them are that checking isn't just at a value level, but at a type level also. Here I'm using it to distinguish between the Some
and None
type, then I can extract the variable value in Some(n)
and make use of it.
main
def fizzbuzzString(end : Int) : String = fizzbuzzList(end).mkString(" ")
def main(args : Array[String]) = println(fizzbuzzString(20))
}
When a file contains an object with a main
method, it will execute that method when it's associated class is called from Java.
Testing in Scala
There are a number of unit test frameworks available for Scala. The one that comes with the system, SUnit, has been deprecated. While there isn't a replacement in the standard libraries, the most common recommendations are:
- ScalaTest: A standard unit testing framework.
- scalacheck: A test generation framework in the same style as Haskell's quickcheck.
- specs: A Behavior-Driven-Design style of test framework.
For my purposes I chose scalacheck because it is well suited to testing the fizzbuzz problem, and I'm rather fond of quickcheck. In scalacheck you write assertions about the function that is tested instead of writing explicit test cases. Scalacheck then generates random data to verify those assertions.
Installing the Library
To install scalacheck, download the jar file (scalacheck-..-.*.jar) from scalacheck's download page and save it to Scala's lib directory, c:\scala\lib
if you're continuing from last week.
Importing Libraries
import org.scalacheck._
import Fizzbuzz._
Import in Scala looks very similar to Import in Java except instead of using *
, you use _
to indicate a wildcard.
Subclassing and Local Imports
object TestFizzbuzz extends Properties("fizzbuzzSingle") {
import Prop._
Scala's subclassing and trait system is extraordinarily complex, but just subclassing from one other class is simple: object <object name> extends <super class>
If you only need to use an import in context to a single class or object, then you can import it inside that class or object.
A scalacheck Properties
property("fizzbuzzSingle: < 1 is None") = forAll
{ (num: Int) => (num < 1) ==> (Fizzbuzz.fizzbuzzSingle(num) == None) }
property("fizzbuzzSingle: >= 1 is not None") = forAll
{ (num: Int) => (num >= 1) ==> (Fizzbuzz.fizzbuzzSingle(num) != None) }
property("fizzbuzzSingle: (mod 3) and not (mod 5) is Some(\"fizz\")") =
forAll (Gen.posNum[Int])
{ (num: Int) => ((num % 3 == 0) && (num % 5 != 0)) ==>
(Fizzbuzz.fizzbuzzSingle(num) == Some("fizz")) }
}
Scalacheck has a lot of depth to it, but getting started is simple. A simple template is:
property("<property description>") = forAll { (<argument> : <type>) => (<limiting condition>) ==> (<assertion>) }
Working backwards, the assertion is a boolean statement that should be true so long as the limiting condition is true. The argument/type combination are the values that you want the system to generate randomly. As in a normal function definition, you can have as many argument/type pairs as you like, separated by commas.
My Experience
Overall I've found working out the fizzbuzz problem in Scala to be interesting and, the language didn't spend much time getting in the way. The two major caveates to that are:
- The state of flux of the unit test system, it'd be nice if they'd pick one to include in the package.
- When the code is compiled, there are an unusually large number of class files because of the anonymous functions that tend to crop up in functional code (14 if you include the test code).