Monday, August 31, 2009

Java vs Scala Control Structures

This topic is mainly for completeness. We will quickly cover the standard control structures you find in Java and see how they are the same or different in Scala.

The first thing to note is that in Scala 2.7 there is no break keyword. In Scala 2.8 there is a break control structure but it is slightly different than the Java break keyword. We will encounter that topic in a later lesson. The control structures I will quickly cover are: do-while, while, for and if.

For information about the Java case statement take a look at the several matching topics covered now and in the future.

Note: The Java ternary if statement does not exist in Scala instead the standard if statement is to be used. It is slightly more verbose but returns a value in the same way as a ternary if statement.
  1. scala>var i = 0;
  2. i: Int = 0
  3. scala>while( i<3 ){
  4.      | println( i )
  5.      | i += 1
  6.      | }
  7. 0
  8. 1
  9. 2
  10. scala> i = 0
  11. i: Int = 0
  12. scala> do {
  13.      | println( i )
  14.      | i += 1
  15.      | } while (i<3)
  16. 0
  17. 1
  18. 2
  19. scala>for(j <- 0 until 3) println (j)  
  20. 0
  21. 1
  22. 2
  23. scala>if (i<3)
  24. more
  25. scala>val result = if (i<3)
  26. result: Int = 10
  27. scala> println (result)
  28. 10
  29. scala>if (i>10) println(1)
  30. scala>if (i<10)
  31. 1
  32. // Note that the return value is ().  You can only get a meaningful return value if there is an else-clause.
  33. scala>val r = if (i<10)>
  34. r: Unit = ()
  35. scala> println(r)
  36. ()

Java Conversions

The collections APIs in Scala are completely separate from those in Java, the primary reason is because they are designed from the ground up to be functional in nature. However, in keeping with Scala's goal of effortless Java integration there are implicits that convert between Java and Scala collections.

The object that needs to be imported has changed between Scala 2.7.x and 2.8 but the usage is the same. In 2.8 all conversions from Java to Scala objects (be it collections, concurrent constructs or others) are in objects call JavaConversions. For example in the August 31 nightly build there are 2 JavaConversions objects. scala.collection.JavaConversions and scala.concurrent.JavaConversions. As time progresses I expect that the types of JavaConversions will expand.

In scala 2.7 you can not convert Scala collections to Java collections only Java to Scala. In 2.8 you can do both.

So here is how to use conversions in 2.7.x:
  1. scala>val jmap = new java.util.HashMap[String,Int]()
  2. jmap: java.util.HashMap[String,Int] = {}
  3. scala> jmap.put("one", 2)
  4. res0: Int = 0
  5. scala> jmap.get("one") 
  6. res1: Int = 2
  7. scala>import scala.collection.jcl.Conversions._
  8. import scala.collection.jcl.Conversions._
  9. scala> jmap("one")
  10. res3: Int = 2
  11. scala> jmap("one") = 1
  12. scala> jmap("one")
  13. res5: Int = 1


And in 2.8:
  1. scala>val jmap = new java.util.HashMap[String,Int]()
  2. jmap: java.util.HashMap[String,Int] = {}
  3. scala> jmap.put("one", 2)
  4. res0: Int = 0
  5. scala> jmap.get("one")
  6. res1: Int = 2
  7. scala>import scala.collection.JavaConversions._
  8. import scala.collection.JavaConversions._
  9. scala> jmap("one")
  10. res3: Int = 2
  11. scala> jmap("one") = 1
  12. scala> jmap("one")
  13. res5: Int = 1

Friday, August 28, 2009

Apply-update methods

The apply and update methods have special meaning in Scala. They allow a developer to define semantics like java array access for an arbitrary class. For example:
  1. scala>class Phonebook {
  2.      |  val numbers = scala.collection.mutable.Map[String,String]()
  3.      | def apply(name:String):String = numbers(name)
  4.      | def update(name:String, number:String) = numbers(name) = number
  5.      | }
  6. defined class Phonebook
  7. scala>val book = new Phonebook() 
  8. book: Phonebook = Phonebook@1e406b09
  9. scala> book("jesse") = "123 456 7890"
  10. scala> println (book("jesse"))
  11. 123 456 7890

As you can see you can invoke the apply method in a similar way that [] are used on arrays in Java. When you call 'obj(param)' the call is translated by the compiler to: 'obj.apply(param)'. As such apply can have any number of arguments.

Similarly 'obj(param) = value' is compiled to 'obj.update(param,value)'. Again the update method can have as many arguments as you wish. However only the last argument is treated as a value. So:
  1. scala>class Echo {
  2.      | def update(n:String, n2:String, n3:String ) = println(n,n2,n3)
  3.      | }
  4. defined class Echo
  5. scala>val e = new Echo()
  6. e: Echo = Echo@2fa847df
  7. scala> e("hello", "hi") = "bonjour"
  8. (hello,hi,bonjour)
  9. scala> e("hello") = ("salut","bonjour")
  10. :7: error: wrong number of arguments for method update: (String,String,String)Unit
  11.        e("hello") = ("salut","bonjour")
  12.                   ^

This makes sense because if apply has many arguments representing the key then the same key must work for the update method for assignment. If you have many values to assign try:
  1. scala>class Phonebook {
  2.      |  val numbers = scala.collection.mutable.Map[String, (Int, Int)]()
  3.      | def apply(name:String) = numbers(name)
  4.      | def update(name:String, number:(Int,Int)) = numbers(name) = number
  5.      | }
  6. defined class Phonebook
  7. scala>val book2 = new Phonebook()
  8. book2: Phonebook = Phonebook@7a120cb3
  9. scala> book2("jesse") = (123, 4567890)
  10. scala>val (areaCode, local) = book2("jesse")
  11. areaCode: Int = 123
  12. local: Int = 4567890

Thursday, August 27, 2009

XPath Style XML Selection

The xml API in scala allows xpath like (although not true xpath) queries. In combination with matching this makes it very easy to process XML documents. I am only going to discuss xpath style selection now. The code section is very long but primarily because the results are often quite lengthy.
  1. scala>val address = <address>
  2.      | <CI_Address>
  3.      | <deliveryPoint>
  4.      | <CharacterString>Viale delle Terme di Caracalla
  5.      | </CharacterString>
  6.      | </deliveryPoint>
  7.      | <city>
  8.      | <CharacterString>Rome</CharacterString>
  9.      | </city>
  10.      | <administrativeArea>
  11.      | <CharacterString />
  12.      | </administrativeArea>
  13.      | <postalCode>
  14.      | <CharacterString>00153</CharacterString>
  15.      | </postalCode>
  16.      | <country>
  17.      | <CharacterString>Italy</CharacterString>
  18.      | </country>
  19.      | <electronicMailAddress>
  20.      | <CharacterString>jippe.hoogeveen@fao.org
  21.      | </CharacterString>
  22.      | </electronicMailAddress>
  23.      | </CI_Address>
  24.      | </address>
  25. address: scala.xml.Elem =
  26. <address>
  27.        <CI_Address>
  28.       ...
  29. // create a pretty printer for writing out the document nicely
  30. scala>  val pp = new scala.xml.PrettyPrinter(80, 5);
  31. pp: scala.xml.PrettyPrinter = scala.xml.PrettyPrinter@6d87c12a
  32. // select the city
  33. scala> println( pp.formatNodes( address \ "CI_Address" \ "city" ) )                   
  34. <city>
  35.      <gco:CharacterString>Rome</gco:CharacterString>
  36. </city>
  37. // a second way to select city
  38. scala> println( pp.formatNodes( address \\ "city" ) )      
  39. <city>
  40.      <gco:CharacterString>Rome</gco:CharacterString>
  41. </city>
  42. // select all characterStrings and print then one per line (unless there is a \n in the text)
  43. scala> (address \\ "CharacterString").mkString( "\n" )
  44. res2: String =
  45. <CharacterString>Viale delle Terme di Caracalla
  46.        </CharacterString>
  47. <CharacterString>Rome</CharacterString>
  48. <CharacterString></CharacterString>
  49. <CharacterString>00153</CharacterString>
  50. <CharacterString>Italy</CharacterString>
  51. <CharacterString>jippe.hoogeveen@fao.org
  52.        </CharacterString>
  53. // iterate over the city node and all of its child nodes.
  54. scala> println( pp.formatNodes( address \\ "city" \\ "_"))
  55. <city>
  56.      <CharacterString>Rome</CharacterString>
  57. </city><CharacterString>Rome</CharacterString>
  58. // similar as above but iterate over all CI_Address nodes and each of its children
  59. scala>println( pp.formatNodes( address \\ "CI_Address" \\ "_")) 
  60. <CI_Address>
  61.      <deliveryPoint>
  62.           <CharacterString>Viale delle Terme di Caracalla </CharacterString>
  63.      </deliveryPoint>
  64.      <city>
  65.           <CharacterString>Rome</CharacterString>
  66.      </city>
  67.      <administrativeArea>
  68.           <CharacterString></CharacterString>
  69.      </administrativeArea>
  70.      <postalCode>
  71.           <CharacterString>00153</CharacterString>
  72.      </postalCode>
  73.      <country>
  74.           <CharacterString>Italy</CharacterString>
  75.      </country>
  76.      <electronicMailAddress>
  77.           <CharacterString>jippe.hoogeveen@fao.org </CharacterString>
  78.      </electronicMailAddress>
  79. </CI_Address><deliveryPoint>
  80.      <CharacterString>Viale delle Terme di Caracalla </CharacterString>
  81. </deliveryPoint><CharacterString>Viale delle Terme di Caracalla </CharacterString><city>
  82.      <CharacterString>Rome</CharacterString>
  83. </city><CharacterString>Rome</CharacterString><administrativeArea>
  84.      <CharacterString></CharacterString>
  85. </administrativeArea><CharacterString></CharacterString><postalCode>
  86.      <CharacterString>00153</CharacterString>
  87. </postalCode><CharacterString>00153</CharacterString><country>
  88.      <CharacterString>Italy</CharacterString>
  89. </country><CharacterString>Italy</CharacterString><electronicMailAddress>
  90.      <CharacterString>jippe.hoogeveen@fao.org </CharacterString>
  91. </electronicMailAddress><CharacterString>jippe.hoogeveen@fao.org </CharacterString>
  92. // print all text
  93. scala> address.text                      
  94. res4: String =
  95.       
  96.       
  97.        Viale delle Terme di Caracalla
  98.       
  99.       
  100.       
  101.        Rome
  102.       
  103.       
  104.       
  105.       
  106.       
  107.        00153
  108.       
  109.       
  110.        Italy
  111.       
  112.       
  113.        jippe.hoogeveen@fao.org
  114.       
  115. // print all character string text
  116. scala> (address \\ "CharacterString").text            
  117. res3: String =
  118. Viale delle Terme di Caracalla
  119.        Rome00153Italyjippe.hoogeveen@fao.org
  120.       
  121. // print all character string text one per line
  122. scala> (address \\ "CharacterString").map( _.text ).mkString("\n")
  123. res6: String =
  124. Viale delle Terme di Caracalla
  125.       
  126. Rome
  127. 00153
  128. Italy
  129. jippe.hoogeveen@fao.org
  130. // find the longest character string
  131. scala> (address \\ "CharacterString").reduceRight(  
  132.      | (elem, longest) => {
  133.      | if( elem.text.length > longest.text.length ) elem
  134.      | else longest
  135.      | })
  136. res8: scala.xml.Node =
  137. <CharacterString>Viale delle Terme di Caracalla
  138.        </CharacterString>
  139. // find the alphabetically first characterstring
  140. scala> (address \\ "CharacterString").reduceRight( (elem, longest) => {
  141.      | if( elem.text > longest.text ) elem
  142.      | else longest
  143.      | })
  144. res9: scala.xml.Node =
  145. <CharacterString>jippe.hoogeveen@fao.org
  146.        </CharacterString>

Tuesday, August 25, 2009

Named and default arguments

NOTE: This topic only works with the up-coming Scala 2.8.0. A beta is expected at the end of September but the nightly builds are quite good.

Named and default arguments are a very nice way to reduce boilerplate code. There are two ideas here.

Default Arguments
This allows one to provide a default value to a method argument so that the caller is not required to provide a value if the default is acceptable. This reduces boilerplate code but also allows the signatures of methods to change and not break existing code (although because of the JVM implementation I think a recompile may be required. I have not verified whether that is the case or not).

This is how it reduces boilerplate code. The java way:
  1. int method( int required, int notRequired) { return required + notRequired; }
  2. int method( int required) { method(required,9); }


2 methods are required and as the number of optional argument increase the number of methods increase exponentially and you will likely need several similarly named methods if the parameters do not have differing type.
  1. def method( required:Int, notRequired:Int=0) = required + notRequired

In Scala 2.8+ you can assign a argument a default value which allows you to optionally provide the value or ignore it.

Named Arguments
The second part of the equation is named arguments. Suppose you have the method and all arguments have different values:
  1. scala>def bigMethod( p1:Int=1, p2:Int=2, p3:Int=3, p4:Int=4, p5:Int=5) = p1 +p2 + p3+ p4 + p5
  2. bigMethod: (p1: Int,p2: Int,p3: Int,p4: Int,p5: Int)Int
  3. scala> bigMethod()  // you have to provide () when default params are used
  4. res10: Int = 15


How can you provide a argument for p3 only? Named arguments to the rescue.
  1. scala> bigMethod( p3 = 10 )
  2. res0: Int = 22

Using named arguments you can declare which arguments you are assigning which value. This works with methods with no default argument as well but is particularly important in conjunction with default arguments.

More examples:
  1. scala>def bigMethod( p1:Int=1, p2:Int=2, p3:Int=3, p4:Int=4, p5:Int=5) = p1 +p2 + p3+ p4 + p5
  2. bigMethod: (p1: Int,p2: Int,p3: Int,p4: Int,p5: Int)Int
  3. scala> bigMethod( p3 = 10 )
  4. res0: Int = 22scala> bigMethod( p3 = 10, p1=11 )
  5. res1: Int = 32
  6. scala> bigMethod( 10,10,10 )
  7. res3: Int = 39
  8. scala> bigMethod( 10,10,10, p5 = -100 )
  9. res5: Int = -66
  10. scala>def anotherMethod( p1:Int, p2:Boolean) = println( p1, p2 )
  11. anotherMethod: (p1: Int,p2: Boolean)Unit
  12. scala> anotherMethod(1,false)
  13. (1,false)
  14. scala> anotherMethod(p2=false, p1=10)
  15. (10,false)
  16. // anotherMethod does not have default args so you must declare all args
  17. scala> anotherMethod(p2=false)
  18. :6: error: not enough arguments for method anotherMethod: (p1: Int,p2: Boolean)Unit.
  19. Unspecified value parameter p1.
  20.        anotherMethod(p2=false)
  21.                     ^

Equals

One welcome change in Scala is the change of semantics of the == operator. In Scala it is the same as doing a .equals comparison in Java. If you want identity comparison you can use the eq method (and ne for the not equal comparator).

Examples:
  1. scala>caseclass Name(first:String, last:String)
  2. defined class Name
  3. scala>val jesse = Name("Jesse","Eichar")
  4. jesse: Name = Name(Jesse,Eichar)
  5. scala>val jesse2 = Name("Jesse","Eichar")
  6. jesse2: Name = Name(Jesse,Eichar)
  7. scala>val jody = Name("Jody","Garnett")
  8. jody: Name = Name(Jody,Garnett)
  9. scala>val jJames = Name("Jesse","James")
  10. jJames: Name = Name(Jesse,James)
  11. scala> jesse == jesse2
  12. res0: Boolean = true
  13. scala> jesse == jody
  14. res1: Boolean = false
  15. scala> jesse ne jesse2
  16. res2: Boolean = true
  17. scala> jesse eq jesse2
  18. res3: Boolean = false

Sunday, August 23, 2009

Enumerations

Scala does not have a enum keyword like java so enumerations are not quite as smooth. Depending on your requirements there are two ways to make enumerations.

  1. Create an object that extends the Enumeration class
  2. Create a case-object hierarchy.
I present both ways. Here are some tips:
  • If you only need discrete and related values without custom behaviour create and object that extends Enumeration
  • If each value has custom information associated with it use case-objects

In the following examples notice the use of the sealed keyword when defining the abstract class MyEnum. Sealed specifies that the heirarchy is sealed. Only classes defined in the same file can extend the class.

Also notice in the case object example that the enumeration values are "case object" not "case class". The two are similar except that there is only one instance of a case object. However all the same boiler plate code is generated and you can still match in the same way.

  1. // Note: MyEnum is an object NOT a class
  2. scala>object MyEnum extends Enumeration("one", "two", "three") {
  3.      | type MyEnumType = Value
  4.      | val One, Two, Three = Value
  5.      | }
  6. defined module MyEnum
  7. scala> MyEnum.One
  8. res1: MyEnum.Value = one
  9. scala>def printEnum( value:MyEnum.MyEnumType ) = println( value.toString )
  10. printEnum: (MyEnum.MyEnumType)Unit
  11. scala> printEnum(MyEnum.Two)
  12. two
  13. // If you don't want to prefix enums with MyEnum. Then you
  14. // can import the values.  This is similar to static imports in java
  15. scala>import MyEnum._
  16. import MyEnum._
  17. scala>def printEnum( value:MyEnumType ) = println( value.toString )
  18. printEnum: (MyEnum.MyEnumType)Unit
  19. scala> printEnum(Three)
  20. three
  21. // Similar but with cases objects
  22. // Notice MyEnum is 'sealed' and the parameters have the 'val' keyword so they are public
  23. scala> abstract sealed class MyEnum(val name:String, val someNum:Int)
  24. defined class MyEnum
  25. scala>caseobject One extends MyEnum("one", 1)
  26. defined module One
  27. scala>caseobject Two extends MyEnum("two", 2)
  28. defined module Two
  29. scala>caseobject Three extends MyEnum("three", 3)
  30. defined module Three
  31. scala>def printEnum(value:MyEnum) = println(value.name, value.someNum)
  32. printEnum: (MyEnum)Unit
  33. scala> printEnum(One)
  34. (one,1)

Thursday, August 20, 2009

Object

Scala does not have static methods in the same way that Java does. As a replacement Scala has "objects" and object is a singleton object whose methods can be called in the same manner one would call a static method in Java. The big differentiator is that objects are complete objects and can extent abstract classes and traits.

Objects are sometime referred to as modules as well. See next section for more on modules. In addition there is a special situation where a class has what is called a companion object. That is a topic for another day. Finally you can have case objects, also a topic for another day. case objects will be address as part of the Enumeration topic.
  1. scala> abstract class SuperClass {
  2.     | def method = "hi"
  3.     | val value = 10
  4.     | }
  5. defined class SuperClass
  6. scala>object MyObject extends SuperClass {
  7.     | overridedef method = "Hello"
  8.     | def anotherMethod = "other"
  9.     | }
  10. defined module MyObject
  11. scala> MyObject.method
  12. res0: java.lang.String = Hello
  13. scala> MyObject.value
  14. res1: Int = 10
  15. scala> MyObject.anotherMethod
  16. res2: java.lang.String = other

Objects are also a good way to modularize projects. You can define classes and other objects within an object
  1. scala>object Outer {
  2.     | caseclass Data(name:String)
  3.     |
  4.     | def print(data:Data) = Console.println(data.name)
  5.     |
  6.     | object Factory {
  7.     | def defaultData = Data("defaultName")
  8.     | }
  9.     | }
  10. defined module Outer
  11. scala>val data = Outer.Factory.defaultData
  12. data: Outer.Data = Data(defaultName)
  13. scala> Outer.print(data)
  14. defaultName

Wednesday, August 19, 2009

Implicit Methods

Scala is designed to be a scalable language and one if the features that assist in realizing this goal are implicit methods. I have seen it occasionally referred to as static duck typing (although I personally think that describes structural typing better).

The idea is to be able to extend an existing class with new methods in a type safe manner. Essentially what happens is a developer defines an implicit method (or imports an implicit method) which converts one object from one type to another type. Once the implicit method is within scope all methods are available from both types.

Note: The conversion is one way. If you want bi-directional conversion you need 2 methods.
  1. scala>class MyInteger( i:Int ) {
  2.      | def myNewMethod = println("hello from myNewMethod")
  3.      | }
  4. defined class MyInteger
  5. // an integer is not a "MyInteger" so you cannot call the myNewMethod on it
  6. scala> 1.myNewMethod
  7. :5: error: value myNewMethod is not a member of Int
  8.        1.myNewMethod
  9.          ^
  10. // define an implicit conversion and now we can call all MyInteger methods on integers
  11. scala> implicit def int2MyInt( i:Int ) = new MyInteger(i)
  12. int2MyInt: (Int)MyInteger
  13. scala> 1.myNewMethod
  14. hello from myNewMethod
  15. scala>class MyInteger2( val i:Int ) {
  16.      | def inc = new MyInteger2( i+1 )
  17.      | }
  18. defined class MyInteger2
  19. // you can define several implicits in an object and import later
  20. scala>object Conversions{
  21.      | implicit def int2MyInt2(i:Int) = new MyInteger2(i)
  22.      | implicit def myInt2Int( mI:MyInteger2) = mI.i
  23.      | }
  24. defined module Conversions
  25. // you can import all implicits    or just    one (although interpreter only allows all for some reason)
  26. scala>import Conversions._
  27. import Conversions._
  28. // inc returns a MyInteger2 instance
  29. scala>val j = 1.inc
  30. j: MyInteger2 = MyInteger2@37f808e6
  31. // test    the bi-directionality of it all    so create a method
  32. // that takes and int
  33. scala>def takesInt( i:Int ) = println(i)
  34. takesInt: (Int)Unit
  35. // j is a MyInteger2 but thanks to the implicit it can be converted back to an int
  36. scala> takesInt( j )
  37. 2
  38. scala> takesInt ( 5.inc )
  39. 6

Imports

Scala's mechanism for importing is analogous to Java's import statements but provides more options. For example import statements can be anywhere in the file and only apply to the scope that they are declared in.
  1. scala>def lineCount = {                                                              
  2.      | import scala.io._ // the '_' character is the wildcard in scala                
  3.      | Source.fromURL("http://www.google.com").getLines.foldRight(0)( (line, acc) => acc + 1 )
  4.      | }
  5. lineCount: Int
  6. scala> lineCount                                                                             
  7. res1: Int = 11
  8. scala>def lineCount = {                                                               
  9.      | import scala.io.Source                                                          
  10.      |  Source.fromURL("http://www.google.com").getLines.foldRight(0)( (line, acc) => acc + 1 )
  11.      | }
  12. lineCount: Int
  13. scala> lineCount
  14. res3: Int = 11
  15. scala>def lineCount = {                                                                                      
  16.      | import scala.io.Source.fromURL // import the fromURL method, only methods from objects can be imported.
  17.      | fromURL("http://www.google.com").getLines.foldRight(0)( (line, acc) => acc + 1 )                       
  18.      | }
  19. lineCount: Int
  20. scala> lineCount
  21. res4: Int = 11
  22. scala>def lineCount = {                                                                                      
  23.      | import scala.io.Source.{fromURL => url} // you can remap imports to another name                       
  24.      | url("http://www.google.com").getLines.foldRight(0)( (line, acc) => acc + 1 )    
  25.      | }
  26. lineCount: Int
  27. scala> lineCount                                                                       
  28. res5: Int = 11
  29. scala>import java.io.{File, FileWriter} // you can import multiple classes in one statement
  30. import java.io.{File, FileWriter}
  31. scala> println( classOf[File], classOf[FileWriter])
  32. (class java.io.File,class java.io.FileWriter)

Monday, August 17, 2009

Return values

As with most functional languages, most control structures ( if, for, try ) return values. The common java idiom:
  1. String name=null;
  2. if( xxx ) name="yyy";
  3. else name="zzz";

can be replaced by
  1. val name = if( xxx ) "yyy"; else"zzz";

The benefit (other than less boiler plate code) is that name can now be a val instead of a var.

Another other point about returns: The return keyword is not required when returning a value from methods or control structures. The last value is always the return value. This is why you will get an error if the last line in a method or control structure is an assignment.

Examples:
  1. scala>val name = if( 1==2 ) "Jesse"else"Mauricio"
  2. name: java.lang.String = Mauricio
  3. scala> println(name)
  4. Mauricio
  5. scala>val collection = for( i <- 1 to 100; if(i%20 == 3) ) yield i
  6. collection: Seq.Projection[Int] = RangeFM(3, 23, 43, 63, 83)
  7. scala> collection.foreach( i => print( i +" ") )
  8. 3 23 43 63 83
  9. scala>val someObj:AnyRef = "Hello"
  10. someObj: AnyRef = Hello
  11. scala>val choice = someObj match {
  12.      | case _:java.io.File => "File"
  13.      | case _:String => "String"
  14.      | case _ => "Dunno"
  15.      | }
  16. choice: java.lang.String = String
  17. scala>val result = try {
  18.      | "two".toInt
  19.      | }catch{
  20.      | case e:NumberFormatException => -1
  21.      | case _ => 0
  22.      | }
  23. result: Int = -1
  24. scala>var i=0
  25. i: Int = 0
  26. // while and do-while do not have return values
  27. scala>while( i<4 ){
  28.      | "22"
  29.      | i += 2
  30.      | }
  31. scala> println( if(i>0) "great"else"less" )
  32. great
  33. // code blocks return the last statement
  34. scala>val m = {
  35.      | val x = 1
  36.      | x + 2
  37.      | }
  38. m: Int = 3

Sunday, August 16, 2009

Assignment and Parameter Objects

One of the principle design goals of Scala is to be "deep" not wide, which means the language attempts to have a small set of rules that can be applied in many different ways in different situations. Pattern matching is one of my favourite examples of this. Pattern matching is commonly seen in match { case... } statements but you will also see it frequently in exception handling, function declaration and, most important for this post, assignment.

Scala does not have multiple assignment like some languages. Instead it has tuples and matching. Tuples are a light-weight way of grouping data in a simple data object. (1,2.0,'c',"string", true). A simple example of a 5 element tuple. Tuples can be up to 22 elements long and can be homogenous or heterogenous. Using this for multiple assignement works something like:
  1. val (firstname, lastname) = ("Jesse","Eichar")

This is pattern matching.
  1. scala> ("Jesse","Eichar") match {
  2.      | case (firstname,lastname) => println(firstname,lastname)
  3.      | }
  4. (Jesse,Eichar)

Notice that in both cases you need the brackets around firstname, lastname. This instructs the compiler that you are matching against a Tuple.

Now the interesting use is with parameter objects. Tuples are a poor substitute for parameter objects because they do not have context. Changing:
  1. def myMethod( firstname:String, middlename:String, lastname:String) = {...}

to
  1. def myMethod( name:(String,String,String)) = {...}

Is not a good change because you loose context. What are the 3 strings? The information must go in the Javadocs. A better option:
  1. caseclass Name(first:String, middle:String, last:String)
  2. def myMethod( name:Name ) = {
  3.   val Name(first, middle, last) = name
  4.   // do something with first middle last
  5. }

The beauty is that you have an object that you can pass around easily. It is a case class therefore extracting the information is incredibly easy and unlike a tuple it has context and can have methods added easily.

Yes it is longer to write but if you need to reuse the data in several locations the trade off is well worth it in clarity.

Examples:
  1. // define name data object.
  2. // Notice toString is a lazy val.  This means value is calculated only once.
  3. scala>caseclass Name(first:String, middle:String, last:String) {
  4.      | override lazy val toString="%s, %s %s" format (last, first,middle)
  5.      | }
  6. defined class Name
  7. // toString formats name nicely.
  8. scala> Name("Jesse","Dale","Eichar")
  9. res1: Name = Eichar, Jesse Dale
  10. scala>def readName() = {
  11.      | //maybe load from a file or database
  12.      | Name("Jesse","Dale","Eichar") :: Name("Jody","","Garnett") :: Name("Andrea","","Antonello"):: Nil
  13.      | }
  14. readName: ()List[Name]
  15. scala>def firstLastName(name:Name) = {
  16.      |  // we are putting _ because we don't care about middle name
  17.      | val Name( first, _, last ) = name
  18.      | (first, last)
  19.      | }
  20. firstLastName: (Name)(String, String)
  21. // convert the list of Names to a list of tuples of first and last name
  22. scala> readName().map( firstLastName _ )
  23. res2: List[(String, String)] = List((Jesse,Eichar), (Jody,Garnett), (Andrea,Antonello))
  24. // print all first names starting with J
  25. scala>for( Name(first,_,_) <- readName; if (first.startsWith("J"))) println(first)
  26. Jesse
  27. Jody
  28. // print the first and middle parts of the first name in the list
  29. scala> readName() match {
  30.      | // the :: _ indicates that we are matching against a list but we don't care about the rest of the list
  31.      | case Name(f,m,_) ::  _ => println( f, m)
  32.      | case _ => println("Not a list of names")
  33.      | }
  34. (Jesse,Dale)

Thursday, August 13, 2009

Traits and inheritance

Scala provides two structures for inheritance. Classes (abstract or not) and traits. Traits are very similar to Ruby Mixins meaning that they can contain code like abstract classes but like interfaces multiple traits can be inherited from.
Like interfaces traits cannot have constructors but in Scala variables can be abstract and therefore provide an easy way to simulate a constructor.
There are no method resolution conflicts because method definitions are always resolved right to left:
  1. class X extends Y with A with B with C

If ABC and Y all have the method (doit) the method in C will be used. If C calls super.doit that will call B.doit and so on.
Note: If Y defines doit the A, B, and C must define doit with the override keyword:
  1. override def doit() = {...}

In the above example ABC must be traits but Y can be a class or a trait. When inheriting you must always have one extends keyword which can optionally followed by one or more with clauses.

  1. scala> abstract class Animal {
  2.      |  val legs:Int
  3.      | val noise:String
  4.      | def makeNoise() = println(noise)
  5.      | }
  6. defined class Animal
  7. scala>  trait Quadriped {
  8.      | self:Animal =>
  9.      | val legs = 4
  10.      | }
  11. defined trait Quadriped
  12. scala> trait Biped {
  13.      | self:Animal =>
  14.      | val legs = 2
  15.      | }
  16. defined trait Biped
  17. scala> class Dog extends Animal with Quadriped {
  18.      | val noise = "Woof"
  19.      | override def makeNoise() = println( noise+" "+noise)
  20.      | }
  21. defined class Dog
  22. scala> new Dog().makeNoise()
  23. Woof Woof
  24. scala> abstract class GenericAnimal extends Animal{ 
  25.      | val noise = "glup"                          
  26.      | }
  27. defined class GenericAnimal
  28. scala> val quad = new GenericAnimal() with Quadriped
  29. quad: GenericAnimal with Quadriped = $anon$1@10bfb545
  30. scala> quad.makeNoise()
  31. glup
  32. scala> val biped = new GenericAnimal() with Biped
  33. biped: GenericAnimal with Biped = $anon$1@7669521
  34. scala> val biped = new GenericAnimal() with Biped{
  35.      | override val noise = "Hello"
  36.      | }
  37. biped: GenericAnimal with Biped = $anon$1@6366ce5f
  38. scala> biped.makeNoise()
  39. Hello

Wednesday, August 12, 2009

Properties

In scala all vars are properties. When compiled to bytecode each var is instead compiled to a getter and a setter (but not a java bean style getter and setter). You generate bean style getters and setters if you add the BeanProperty annotation (that is for another day).

The signature of the generated methods of an Int variable called property are:
  1. def property:Int
  2. def property_=( newVal:Int):Unit

Because of this you can declare the property as a var and in the future if you decide that you need behaviour in a setter and getter you can introduce those two methods in place of the single var and no client code is broken.
  1. scala>class C1( var property:Int )
  2. defined class C1
  3. scala>class C2( dependency:C1 ){
  4.      | println(dependency.property)
  5.      | dependency.property += 1
  6.      | println(dependency.property)
  7.      | }
  8. defined class C2
  9. scala>new C2(new C1(10))
  10. 10
  11. 11
  12. res0: C2 = C2@72e28a61
  13. scala>  class C1( privatevar p:Int) {
  14.      | def property = p
  15.      | def property_=(newVal:Int) = p = newVal * 100
  16.      | }
  17. defined class C1
  18. scala>  class C2( dependency:C1 ){
  19.      | println(dependency.property)
  20.      | dependency.property += 1
  21.      | println(dependency.property)
  22.      | }
  23. defined class C2
  24. scala>new C2(new C1(10))
  25. 10
  26. 1100
  27. res0: C2 = C2@62c639ce

Notice C2 uses the same API to access C1.property.

Tuesday, August 11, 2009

For-comprehensions

The for-comprehension construct is a very powerful way of iterating over collections. In its most basic form it is the java for( var: collection){} loop. As with all flow constructs in Scala, the scala for loop (or more correctly for-comprehension) can return a value. In the case of the for-comprehension it returns Unit (similar to void in Java terms) or a Sequence if yield is used.
  1. scala>val range = 1 to 5
  2. range: Range.Inclusive = Range(1, 2, 3, 4, 5)
  3. // no return value if there is no 'yield' keyword
  4. scala>for( i <- 1 to 10 ) { i + 1 }
  5. // if there is a yield a collection is returned
  6. // the type of collection depends on the input
  7. // here a Range is returned
  8. scala>for( i <- range ) yield i+1
  9. res1: RandomAccessSeq.Projection[Int] = RangeM(2, 3, 4, 5, 6)
  10. // here a list is returned
  11. scala>for( i <- List( "a", "b", "c") ) yield"Word: "+i
  12. res1: List[java.lang.String] = List(Word: a, Word: b, Word: c)
  13. // you can filter the elements that visited in the loop
  14. scala>for( i <- range; if( i % 2 == 0) ) yield i
  15. res2: Seq.Projection[Int] = RangeFM(2, 4)
  16. // this    is more    about creating ranges than loops
  17. scala>for ( i <- 20 until (10,-2) ) yield i
  18. res3: RandomAccessSeq.Projection[Int] = RangeM(20, 18, 16, 14, 12)
  19. // you can string together multiple "generators"
  20. scala>for( i <- range; j <- range) yield (i,j)
  21. res4: Seq.Projection[(Int, Int)] = RangeG((1,1), (1,2), (1,3), (1,4), (1,5), (2,1), (2,2), (2,3), (2,4), (2,5), (3,1), (3,2), (3,3), (3,4), (3,5), (4,1), (4,2), (4,3), (4,4), (4,5), (5,1), (5,2), (5,3), (5\
  22. ,4), (5,5))
  23. // you can also    declar variables as part of the    loop declaration
  24. scala>for( i <- range; j <- 1 to i; k = i-j) yield k
  25. res5: Seq.Projection[Int] = RangeG(0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0)
  26. // with round brackets '(' and ')' multiple lines will require semi-colons
  27. scala>for (
  28.      | i <- range;
  29.      | j <- 1 to i;
  30.      | k = i-j) yield k
  31. res6: Seq.Projection[Int] = RangeG(0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0)
  32. // with curly brackets '{' and '}' multiple lines you do not require semi-colons
  33. scala>for {
  34.      | i <- range
  35.      | j <- 1 to i
  36.      | k = i-j}
  37.      | yield{
  38.      | k
  39.      | }
  40. res7: Seq.Projection[Int] = RangeG(0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0)
  41. scala>for( i <- "enjoy" ) print(i)
  42. enjoy