Friday, February 11, 2011

Learning Scala

I ventured to learn Scala few weeks ago (my new year + long postponed resolution), inspired by its balanced hybrid functional programming and object oriented approach, it's basically like Java on steroids. Scala has so many great features, I can not begin to mention what captivated me from the moment I started reading Martin Odersky's book + book + book . This post is aimed to share my first steps and my discoveries, perhaps it can help you if you happen to be at the early stages as well. Primarily what captured my attention was the fact that you can extend the language, in this post we'll see later on how you can code control structure abstractions, this concept has a lot of potential for exploration! I'll have to admit that my brain is used to think OO the imperative way, as most of us that are Java developers for years, the functional syntax at first bends your mind and tricks your logic, but with time things start blending and sinking in. You'll probably still notice in my code examples I still tend to approach coding with imperative tone, so please forgive my short-comings :) I'd like to begin with this and SBT, here are few simple tasks that I added to my first project, just drop this example under /projroot/project/build/build.scala , and you'll see the tasks when invoking sbt at the shell and then 'actions', the 'meta' task is helpful to dump internal settings such as repos, cp, etc, I really like this tool, simple concise and extendible, you can configure it to manage dependencies and multiple sub projects, etc.
import sbt._ , sbt.Process._ , Process._  
class MySBT(info: ProjectInfo) extends DefaultProject(info) {

val home = Path.fromFile(System.getenv("HOME"))
val libRepo = home / "projs" / "repository" / "lib" ** "*.jar"
//override def unmanagedClasspath = super.unmanagedClasspath +++ libRepo   
//override def mainClass = Some("org.martin.cli.ScellMain")
//override def compileOptions = super.compileOptions ++ compileOptions("-verbose","-optimize") 
override def artifactID = "xxx"

lazy val q    = task { println("bye\n");System.exit(0); None } describedAs("quit")
lazy val env  = task { "env" ! log; None } describedAs("dump env vars")
lazy val ll   = task { "ls -al" ! log; None } describedAs("list all files @ dir")
lazy val fjar = task {
"find project -name *.jar" ! log
None
} describedAs("find project -name *.jar")

lazy val xb = task {
"./bin/scalaxb -d ./xsd -p com.company.sso -v ./xsd/sso.xsd" ! log
None
} describedAs("scalaxb stub generation")

lazy val meta = task {
println("repositories");
repositories.foreach(println)
println("ivyRepositories repositories")
ivyRepositories.foreach(println)
println("compilation options");
compileOptions.foreach(println)
println("compilation CP")
println(compileClasspath)
println("test CP")
println(testClasspath)
println("main external dependencies")
println(mainDependencies.external)
println("main libs dependencies")
println(mainDependencies.libraries)
println("scala dependencies")
println(mainDependencies.scalaJars)
println("unmanagedClasspath")
println(unmanagedClasspath)
None
} describedAs("dump of main meta properties.")

//...more tasks !!
}
Next, I started coding samples in the $scala interpreter snippets from the books, I found this video an excellent and fun resource REPL, so below is a 'script.scala' skeleton for drop and enhance for your needs, it contains few utility you might find useful.
#!/bin/sh
echo "    ________ ___   __   ___     Scala Shell Script"
echo "   / __/ __// _ | / /  / _ |"
echo " __\ \/ /__/ __ |/ /__/ __ |"
echo "/____/\___/_/ |_/____/_/ |_|"
exec scala "$0" "$@"
!#
//////////////////////////////////////////////////////////////////////
lazy val home    = System.getenv("HOME")
lazy val usr     = System.getenv("USERNAME")
lazy val pwd     = System.getProperty("user.dir",".")
lazy val host    = java.net.InetAddress.getLocalHost.getHostName
lazy val props   = System.getProperties().list(System.out)
// props
//////////////////////////////////////////////////////////////////////
for( arg <- args ) println( arg )
//////////////////////////////////////////////////////////////////////
println( usr + "@" + host + ":" + (new java.util.Date()) + ":" + pwd )
//////////////////////////////////////////////////////////////////////
// below is a naive interpreted script for demo
import java.io.File

def files = (new File(pwd)).listFiles

def filesMatching(query: String, matcher: (String, String) => Boolean) = {
for (file <- files; if matcher(file.getName, query)) yield file
}

def filesEnding(query: String)     = filesMatching(query, _.endsWith(_))
def filesContaining(query: String) = filesMatching(query, _.contains(_))
def filesRegex(query: String)      = filesMatching(query, _.matches(_))

def dump( list:List[File] ) { for ( x <- list ) println( x.toURI ) }

dump( filesEnding("*").toList )
dump( filesContaining("scala").toList )
dump( filesRegex(".*").toList )
///////////////////////////////////////////////////////////////////////
Once I run many book samples I began writing a small sample project, here's where I found a lack of tooling in the IDE arena, since I use Eclipse I soon noticed this plug-in is coming along but has few rough edges here and there, I'm sure it's just a matter of time. I find very handy launching from the interpreter os bound commands such as: scala> :sh gedit source_file.scala but that's just a matter of personal preference :) You can take agile approach by launching the interpreter from your IDE, so at your fingertips you can perform REPL for quick snippet PoCs, then pseudo-code a TDD, then code targeted implementations, then materialize tests, then repeat cycle from step#1 etc, continuous building, testing, bug fixing, etc => profit!! David Pollack's book pointed out a very elegant approach with a custom control structure that I adopted in my first project, the capability to augment functionally targeted behaviour, in other words let's look at the below Java sample serialising an obj to the file system, this sample is coded this way to depict the point, notice the convenient closeStream()
public void write( final String file , final Serializable serializable )
throws IOException {
final FileOutputStream     fos = new FileOutputStream( file );
final BufferedOutputStream bos = new BufferedOutputStream( fos );
final GZIPOutputStream     zos = new GZIPOutputStream( bos );
final ObjectOutputStream   out = new ObjectOutputStream( zos );
out.writeObject( serializable );
closeStream( out );
closeStream( zos );
closeStream( bos );
closeStream( fos );
}
public void closeStream( final OutputStream stream ) {
try {
   if ( stream != null )
  stream.close();
 } catch (final Exception e) { }
}
It'd be nice if we could delegate the control to an abstraction via separation of concern so it can do it for us instead, this 'loan pattern' could be handy if consumers of your API could potentially forget to close resources such as tcp /db connections (very typical to see that in poorly written direct jdbc + io etc ): let's see how functional can help here
def streaming[T <:{def close():Unit}, R](target: T)(func: T => R): R =
try {
  func(target)
} finally {
  try {
  target.close()
  println("closed "+target)
 } catch { case _ => }
}
Don't worry too much on the syntax above, we'll review it later, by looking below at read() you'll see the benefits, syntax looks concise and cleaner:
def read[T]( file:String ) : T = {
streaming( new FileInputStream( file ) )   { fis =>
 streaming( new BufferedInputStream( fis ) ){ bis =>
  streaming( new GZIPInputStream( bis ) )    { zis =>
   streaming( new ObjectInputStream( zis ) )  { ois => {
    val t = ois.readObject.asInstanceOf[T]
    println("read " + t)
    t
    }
   }
  }
 }
}
}

def write( file:String , ref:AnyRef ) : Unit = {
require( ref.isInstanceOf[Serializable] , "not @serializable T" )
streaming( new FileOutputStream( file ) )   { fos =>
 streaming( new BufferedOutputStream( fos ) ){ bos =>
  streaming( new GZIPOutputStream( bos ) )    { zos =>
   streaming( new ObjectOutputStream( zos ) )  { out =>
    out.writeObject( ref )
    println("wrote " + ref )
   }
  }
 }
}
}
Let's review the control parameterised signature: def streaming[ T , R ]( target: T )( func: T => R ): R = { .. } type T func(target) returns a type R which R can then be accessed within scope def streaming[T <:{def close():Unit}, R](target: T)(func: T => R): R = {.. } now with a structural type bound on type T that must have public 'close' method in T. Scala allows you to define types based on their structure and pass functions as parameters, which is a very powerful thing.
@serializable
@SerialVersionUID(1234L)
class SerializableType(
   private val id: Int,
 @BeanProperty val name: String,
 @BeanProperty val list: List[Int] ) {
 val tm: Long = System.currentTimeMillis
 override def toString() = {
 tm + " ID: " + id + " - " + name + " : " + list
}
}

object IoDemo {
def main(args: Array[String]) = {
import org.martin.scala.{SerializableType => Pojo} // aliased
val ser:IoDemo = new IoDemo
val file = "/tmp/scala.gzip"
val t = new Pojo(100,"serialization test",List(1,2,3,4,5))
ser.write( file , t )
val tt = ser.read[Pojo]( file )
}
}
Here we have a pojo class with @serializable annotation, one being a List (immutable) collection and a companion object (singleton) with a main() as a poor-man-test, if then compiled and run it yields:
wrote 1297557333613 ID: 100 - serialization test : List(1,2,3,4,5)
closed java.io.ObjectOutputStream@1a786c3
closed java.util.zip.GZIPOutputStream@18088c0
closed java.io.BufferedOutputStream@1922221
closed java.io.FileOutputStream@191d8c1

read 1297557333613 ID: 100 - serialization test : List(1,2,3,4,5)
closed java.io.ObjectInputStream@c832d2
closed java.util.zip.GZIPInputStream@1808199
closed java.io.BufferedInputStream@1bc887b
closed java.io.FileInputStream@a46701
We can now see from output how the streams are closed accordingly in relevant order. Below is another handy one, with conditional logic, appending to a mutable list buffer of T and returning an immutable list of T.
def whiletrue[T]( continue : => Boolean )( in: => T ): List[T] = {
val buffer = new ListBuffer[T]
  while( continue ) buffer += in
buffer.toList
}
Conclusion, imagine taking these concepts even further, combining them with even more abstractions, by now you should have a taste of some functional approach and few of so many great Scala features, I could enumerate some that stand out: type inference, foster immutable state, pattern matching, actors, traits, closures, implicit conversions, and the list goes on. Learning a new language is a quite an experience, you need to step out of your comfort zone and take a whole bunch of new stuff in, after few weeks of reading and coding in Scala, I can tell you that it's been an amazing venture for me, and the journey has just begun.

1 comments:

Martin Zoldano said...

a bit too late in the game but here is the JDK7 approach:
http://download.java.net/jdk7/docs/technotes/guides/language/try-with-resources.html