Pragmatic Scala

Dave Thomas, one half of the renowned Pragmatic Programmers, has written a series of posts introducing Erlang. It is a pretty cool programming language, geared towards highly concurrent applications, with special care for fault tolerance. In fact, Erlang was created by a team at Ericsson as a way to program their powerful telecom systems. I'm quite intrigued by it, specially since it has roots in Prolog\*, a very underestimated language nowadays.

But I don't want write about Erlang, for (a), I don't know much about it and (b), PragDave is doing a great job with his tutorials. So, why the hell am I writing this post? The answer is that the few lines of Erlang I've seen reminded me of Scala, my current language of choice. Quoting myself:

“Scala, [is] a programming language developed at a Swiss university. It has many (many!) cool features, such as seamless interoperability with Java - a result of being compiled to JVM bytecodes -, strong support for functional programming, sophisticated object oriented characteristics and a strong static type system.”

So, let's try to translate Dave's first tutorial to Scala. Our goal is to write a function that takes an ISBN code and returns a pair with the book's title and sales rank from Amazon.com. The first step was to code a little skeletal function, to get acquainted with the environment.

object BookSales {
   def fetchTitleAndRank(ISBN:String) =
      ("Agile Web Development with Rails", 1234)
}

This is very similar to Dave's code, except for that object BookSales thingamajig, what's the deal with that? Well, Scala is an object-oriented language, and this means that we can't have top-level functions floating around. If you program in Java and know your GoF, you can think of the object keyword as language support for the Singleton pattern. If you are coming from the functional programing planet, (where I am but a tourist :) I'm told that this is akin to modules in ML. The easiest way to run this code is to open the scala interpreter, creatively named “scala”, and type in the following two commands:

scala> :load BookSales.scala
Loading BookSales.scala...
defined module Http
defined module BookSales

scala>; BookSales.fetchTitleAndRank("0977616630")
line2: (java.lang.String, java.lang.String) = (Agile Web Development with Rails (Pragmatic Programmers),1234)

Following Dave's lead, now is time to build a URL that will be used to call the Amazon Web Services REST interface (its not really REST, but that's how they call it...). A couple more members in BookSales should do the trick\*\*:

object BookSales {
   //...
   val baseURL = "http://webservices.amazon.com/onca/xml?" +
         "Service=AWSECommerceService" +
         "&SubscriptionId=YOUR_KEY_HERE" +
         "&Operation=ItemLookup" +
         "&ResponseGroup=SalesRank,Small" +
         "&ItemId="
         
    def amazonUrlFor(ISBN:String) = baseURL + ISBN
}

Remember to replace your real amazon key for the SubscriptionId value. The next step Dave took was to hunt around for some http client library in Erlang. With Scala we have the benefit of just using java.net and java.io! The following would do just fine:

val bodyInputStream = new URL(amazonUrlFor(ISBN)).openStream

But I want to emulate more closely the Erlang version, so we'll write a new object with a single method for retrieving a pair (status, requestBody), just like in Dave's code.

object Http {
   import java.io.InputStream;
   import java.net.URL;
   
   def request(urlString:String): (Boolean, InputStream) = 
      try {
         val url = new URL(urlString)
         val body = url.openStream
         (true, body)
      }
      catch {
         case ex:Exception => (false, null)
      }
}

I don't want to go into detail about this code; remember, it is just here so we can pretend we are in Erlang. Now let's just go ahead and flesh out our fetchTitleAndRank method.

object BookSales {
   import scala.xml._;
   import java.net._;

   def fetchTitleAndRank(ISBN:String) = {
      val (true, body) = Http request amazonUrlFor(ISBN) /\* 1 \*/
      val xml = XML.load(body)          /\* 2 \*/
      val rank = xml \\\\ "SalesRank"     /\* 4 \*/
      val title = xml \\\\ "Title"        /\* 5 \*/
      (title.text, rank.text)          /\* 6 \*/
   }
  //...
}

I numbered the lines with comments so we can talk about each one, as there is a lot going on here. First, on line 1 we call our Http.request method passing the constructed URL as a parameter. Note that the call syntax is a little different from what we've seen so far, there is no annoying punctuation to indicate the method call – this is a common syntactic variant supported by the Scala language. It comes in handy when developing DSLs. Ok, still on line 1 we have a pattern matching assignment. It's behavior is very similar to Erlang's as described in Dave's post. Be warned, though, that pattern matching is not commonly done in this manner, and passing a pair with a status code member is definitely not a common Scala idiom. We normally would use exceptions for exceptional situations, just like in Java, or return an Option if some kind of “null” outcome is to be expected. The next line just parses the received response using Scala's awesome XML library. A quick sample of its awesomeness\*\*\* can be seen in the 4th and 5th lines. We are doing an XPath-like query over the document, much like the Erlang code does. But, instead of coding the query inside a String, we actually call a \\\\ method that has the same effect. Cool little snippet, isn't it?

I guess this is a good time to remember that we are working in an Statically typed language. Did you notice those that all parameter declarations were appended by a “:Type” annotation? That's because Scala's compiler can't safely infer the types of the parameters. Everything else, including local variables and method return types can be magically infered by Scalac. Of course, when maintainability is a concern, we can explicitly write out some of the types, as we did in Http.request



Here is the whole program:

object Http {
   import java.io.InputStream;
   import java.net.URL;
   
   def request(urlString:String): (Boolean, InputStream) = 
      try {
         val url = new URL(urlString)
         val body = url.openStream
         (true, body)
      }
      catch {
         case ex:Exception => (false, null)
      }
}

object BookSales extends Application {
   import scala.xml._;
   import java.net._;

   def fetchTitleAndRank(ISBN:String) = {
      val (true, body) = Http request amazonUrlFor(ISBN)
      val xml = XML.load(body)
      val rank = xml \\\\ "SalesRank"
      val title = xml \\\\ "Title"
      (title.text, rank.text)
   }
   	
   val baseURL = "http://webservices.amazon.com/onca/xml?" +
         "Service=AWSECommerceService" +
         "&SubscriptionId=YOUR_KEY_HERE" +
         "&Operation=ItemLookup" +
         "&ResponseGroup=SalesRank,Small" +
         "&ItemId="
         
    def amazonUrlFor(ISBN:String) = baseURL + ISBN
}

To find out more about Scala, check out the material at scala-lang.org and Sundararajan's Scala for Java programmers articles.



\*I must note that, though it incorporated some Prologisms, Erlang is not, AFAIK, a logic programming language.

\*\* Scala does support HEREDOCs kind of literals with the triple-quote syntax “””, so you could replace the concatenations with a big literal and a .replaceAll(...) at the end to remove the whitespace, if that's your kind of thing.

\*\*\* Note to self: get a good dictionary before next blog post to avoid the embarrassment from writing stupid stuff like "awesomeness".

Comments:

Interestingly, Scala also added support for both thread and continuation based actors in its most recent versions, based on work by Phillip Haller (sp?). Making it even more Erlangish...

Posted by Max on April 26, 2007 at 02:29 AM BRT #

.. sorry but erlang version is just a lot more beautiful but that's just IMHO

Posted by sorry about this on April 26, 2007 at 05:19 AM BRT #

I disagree aboutthe Erlang looking better. The XML parsing in particular is IMO much nicer in Scala.

Posted by Henry on May 02, 2007 at 08:29 AM BRT #

Instead of (Boolean, InputStream) better to use Some[InputStream]

Posted by Yura on July 09, 2009 at 09:21 PM BRT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

rafaeldff

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today