Showing posts with label clojure. Show all posts
Showing posts with label clojure. Show all posts

Wednesday, January 5, 2011

Clojure versus Scala (part 2)

In my previous post, I went over all of the basics introduced by the authors of "Clojure: functioneel programmeren". In the second part of their first article, they build a Last.fm client, based on the programming concepts introduced before. Let me do the same thing for Scala.

Build environment


Clojure has Leiningen, but I bet Maven is supported as well. Same goes for Scala: there are people using Rake or Gradle, and of course there's SBT (discussed before). However, for people coming from a Java world, Maven works just as well.

So, to start a Scala Maven project, just type this on the commandline:

mvn archetype:generate -DarchetypeCatalog=http://nexus.scala-tools.org/content/groups/public

... and choose the simple Scala project. Fill out the basic details, and you will have something working. (Now, this is a command that you're going to use more often. This might be a good time to turn it into a key macro.)

In order to make sure you can download the proper libraries, you obviously need to add the repo and a dependency:


        
            xebia-maven
            http://os.xebia.com/maven2
        
    
...
    
        
            net.roarsoftware
            last.fm-bindings
            1.0
        

Namespace


The next thing the authors do is talk about namespaces for a while. They mention that in Clojure, namespaces are first-class citizens. I guess the same applies to Scala as well. However, you cannot add new symbols to a package, as Clojure allows you to do.

Listing the top tracks


This is the Clojure version:

(defn top-tracks
  [user-name api-key]
  (User/getTopTracks user-name api-key))

This is the Scala version:

def topTracks(user: String, apiKey: String) =
  getTopTracks(user, apiKey).toSeq

Now, the above only works since I imported all of the User's functions somewhere else (so getTopTracks has been pulled into scope). And I can only invoke toSeq on the results of getTopTracks (normally a java.util.Collection) because of an import of some implicit conversions:

import net.roarsoftware.lastfm.User._
import net.roarsoftware.lastfm.Track
import scala.collection.JavaConversions._

Converting Track to a String


This is the Clojure version:

(defn track-to-str
  [track]
  (let [track-name (.getName track)
        artist-name (.getArtist track))
  str track-name " by " artist-name)))

This is the Scala version:

def trackToString(track: Track) =
    track.getName + " by " + track.getArtist

Numbering a list of items


This is the way the authors do it in Clojure:

(defn number-a-sequence
  [seeq]
  (map-indexed #(str (+ 1 %1) " " %2) seeq))

This is the Scala version. Basically, what it's doing is first create a sequence of tuples, existing of the element itself followed by its index. And then it goes on to map each individual item to a String.

def numberASequence(seq: Seq[Any]) =
  seq.zipWithIndex.map({
    case (elem, index) => (index + 1) + " " + elem
  })

Building HTML


Again, Clojure:

(defn to-html
  [str-seeq]
  (let [ header "<html><body>"
         footer "</body></html>"]
    (str header (reduce str (map #(str % "< br />") str-seeq)) footer )))

And this is Scala:

def toHtml(list: Traversable[Any]) =
  <html>
    <body>{list.map(item => <p>{item}</p>)}</body>
  </html>

In this case, it might be worth noting that the Scala version is actually building XML, whereas the Clojure version is generating a String. Building XML is a little safer: if the text included in your XML contains special characters, then Scala's XML support will guarantee that those special characters are getting escaped properly. (Who knows, perhaps there is an artist called "".)

Conclusion


This basically constitutes everything discussed in the Clojure article. They conclude that a Clojure program like this required less than 25 lines of code. I think it's fair to say that both Scala and Clojure are in good shape in that regard. I have counted the LoC of the Scala version, and it adds up to 22.

So, which one is the winner? I think it's inconclusive. I like the fact that Scala is statically typed, without a significant penalty. The number of lines of code is roughly the same. What do you think?

(Full source code is here.)

Tuesday, January 4, 2011

Clojure versus Scala (part 1)

This is just a brain dump, after having read an excellent article on Clojure by Maurits and Sander in the Dutch Java magazine. Admittedly, without having access to the original article, it probably isn't of any use, but I just wanted to jot it down here, for future reference.

Hello World


Let's start with their simple Hello world example. This is the Clojure version:

(def hello (fn [target] (println (str "Hello " target))))
(hello "Clojure")

or the short version:

(defn hello [target] (println (str "Hello " target)))
(hello "Clojure")

... and this is the Scala version - even shorter:

def hello(target: Any) = println("Hello " + target) 
hello("Scala")

I'm happy to say that the Scala version is shorter in number of characters, even though the type of parameter 'target' had to be specified explicitly.

Doc String


Next they explain the purpose of the doc string. Now this is something that I truly miss in Scala. It would be totally awesome to have the ability to pull up documentation on a function from the REPL, but it doesn't exist. Scala does have scaladoc, but that's all thrown away at compilation time. It should be possible to store some of this in AttributeInfo, but it doesn't.

This is clearly an area in which Clojure's ancestry of LISP and Scala's ancestry of Java shows.

First class functions


Before going any further, we first need to have square function, as defined in the article like this:

(defn square [x] (* x x))

Now, I would love to say that defining square in Scala is just as easy. If you would only define square for integers, it would be:

def square(x: Int) = x * x

... but we obviously want it to work for doubles as well, as I suspect the Clojure version does.

Now, this is the Scala version that supports any numeric type:

def square[T](x: T)(implicit numeric: Numeric[T]): T = 
  numeric.times(x, x)

in Scala 2.8.1 there is a shorthand notation for the same thing:

def square[T: Numeric](x: T) = 
  implicitly[Numeric[T]].times(n, n)

Not quite as intuitive as you would have expected, but it works quite well:

scala> square(4.0)
res1: Double = 16.0

scala> square(5)
res2: Int = 25

Once the square function has been defined, the article explains that functions like square could be passed to a function called twice, with twice being defined like this:

(defn twice [f a] (f (f a))) 
(twice square 2)

This is the Scala version:

def twice[T](a: T)(f: (T) => T) = f(f(a)) 
twice(4)(square)

It may look a little awkward at first, but this is the only way you get it to work without having to pass in additional type information. (Check this for more information.)

Here's an alternative that does not work:

scala> def twice[T](a: T, f: (T) => T) = f(f(a)) 
twice: [T](a: T,f: (T) => T)T

scala> twice(4, square)         
:8: error: could not find implicit value for evidence parameter of type Numeric[T]
       twice(4, square) 
                ^

If you insist on defining twice like this - perhaps because of its resemblance to the Clojure version, then the only option you have is call twice like this:

scala> twice(square[Int], 2)
res70: Int = 16

Data structures


Onward to lists. Clojure example to produce a list:

(list 1 2 3)

... and then a couple of alternatives for doing a Scala List:

List(1, 2, 3)
1 :: 2 :: 3 :: nil

Adding an item to a list in Clojure:

(conj (list 1 2 3) 4)

... and the same in Scala:

4 :: List(1, 2, 3)

In Scala, Lists are typed. You can only add anything to a List if it's a List[Any]. So, this is fine:

scala> 1 :: "a" :: 2 :: "b" :: Nil
res74: List[Any] = List(1, a, 2, b)

scala> List(1, "a", 2, "b")
res75: List[Any] = List(1, a, 2, b)

While Clojure does not allow you to directly access one of the elements unless you use a vector, Scala does allow you to get the nnth element of a list:

scala> val list = List(1, "a", 2, "b")
list: List[Any] = List(1, a, 2, b)

scala> list(0)
res80: Any = 1

scala> list(2)
res81: Any = 2

scala> list(1)
res82: Any = a

Now, even though Scala does allow you to do it - that doesn't mean you should feel encouraged to code like that; it also defines a Vector, which is - just like the one in Clojure - way more optimized for random access.

Maps


This is the Clojure version of defining a map:

(def mymap '{:aap "monkey" :ezel "donkey" :walvis "whale" :onbekend "platypus"})

(:ezel mymap)

... and the Scala version:

val mymap = Map("aap"->"monkey", 
  "ezel"->"donkey", 
  "walvis"->"whale", 
  "onbekend"->"platypus")
mymap("ezel")

Updating the map is a little different than what you are used to, if you come from the Java space. In fact, in that sense, it's not unlike Clojure. In Clojure, you update a map like this:

(assoc mymap :onbekend "unknown")

In Scala it's done slight differently, but the net effect is the same: a new map, containing all of the previously defined entries plus a new one.

scala> mymap + ("onbekend" -> "unknown")
res90: scala.collection.immutable.Map[java.lang.String,java.lang.String] = Map((aap,monkey), (ezel,donkey), (walvis,whale), (onbekend,unknown))