Off the Wall Is Scala really more complicated than Java? by Dick Wall September 18, 2009
Summary
After a conversation with a guy at work, I was inspired to take a look at whether Scala is inherently more complex than Java, and decided it wasn't.
Advertisement
Today, I was asked by a friend to provide an alternative to the following Java one-liner:
This is a one-liner that unsurprisingly prints out the happy birthday song. Easy enough. So, I set about doing something in scala. While at it, I had a little fun and made a few "improvements". The result was this:
(1 to 4).map { i => "Happy Birthday %s".format(if (i == 3) "Dear XXX" else "To You") }.foreach { println(_) }
When another friend saw this, he immediately opined that the Scala version was too complicated and would be hard to maintain. An examination, I feel, is clearly called for here.
Why does it look so wierd?
Well, there are a number of reasons for why this looks hard to understand. The most obvious one is unfamiliarity. We have had Java around for 15+ years now, and we all know the syntax really well here. I would dissect a couple of parts and examine them in isolation to illustrate.
for(int i=0; i<4; i++) {}
It's familiar to all of us, but really would we hold it up as a perfect example of readability. Someone with no C or Java experience might wonder what the statement actually does. We all know the first part is the initialization, the second is the termination test, and the third is the increment, but compare this with something like:
for i in 1 to 4 begin ... end
Which is what you would see in BASIC. Java has more than it's fair share of "lore", it's just we seem to forget about that.
Now Scala actually has several options for this kind of loop, perhaps a more fair comparison in my example would have been:
for (i <- 1 to 4) { }
(I tend to read <- as "from") so: for i from 1 to 4 - pretty readable and understandable, I would say moreso than the Java one, if you were cold on both languages. However, that's not the form I used, I instead used the more universal.
(1 to 4).map { }
I could have, more fairly, used
(1 to 4).foreach { }
Which I think is very readable - the reason for using map instead will be explored below, but if I was to write it using foreach directly, we could end up with:
(1 to 4).foreach {i => println ("Happy Birthday " + (if (i == 3) "Dear XXX" else "To You")) }
Alternatively, I could have used:
for (i <- 1 to 4) println ("Happy Birthday " + (if (i == 3) "Dear XXX" else "To You"))
Now, that's pretty reasonable, so why not stick with that version? Before we go on, I would like to point out that I also consider the ternary operator in Scala superior in readability to Java. Just compare:
i == 3 ? "A" : "B"
Kind of looks like line noise until you are used to it, now how about
if (i == 3) "A" else "B"
now that's more like it. Again, if you aren't familiar with either language, which is the easier to read? As an added bonus, there is no difference between the syntax of a typical if else statement and the ternary operator, I mean, they do the same thing, so why make them different?
So, given that, why didn't I stick with the simple, readable version. I could have easily done so, functionally it is directly equivalent to the Java version, and therein lies the (maybe a little belabored) point.
Most programs don't print stuff out to Standard Out these days, they might log it, but mostly what they do is prepare data to go into user interfaces. As such, the Java for loop is a bit useless. Let's see how we would actually go about making the sentences that make up the song in Java. I think the following would be the most compact and best signal to noise ratio:
String[] birthdayArray = new String[4];
for (int i = 0; i < 4; i++) { birthdayArray[i] = "Happy Birthday " + (i==2 ? "Dear XXX" : "To You"); }
(Most of the time you want something more durable like a list rather than an array for all sorts of good reasons, but you could just stop at an array if you wanted one).
We could put all of these statements onto one line of course, but I think readability would suffer more. The good news is, we could now use the list of strings to populate something in a GUI or web page with nice markup. This is more representative of what we do as developers every day.
What would this look like in Scala?
val birthdaySong = (1 to 4).map { i => "Happy Birthday " + (if (i==3) "Dear XXX" else "To You") }
Now we are starting to see the worm turn! The above is completely type-safe (don't be fooled, the compiler knows that birthdaySong contains a list of strings at the end of this), however we don't have to initialize a structure to hold the results, nor supply any type information since the compiler can infer it.
Look at the example again, and maybe you will start to see that the syntax looks less alien to you after a while. Certainly I think that the signal to noise ratio is higher than with the Java equivalent.
I also don't like that addition of strings. It works if you are adding something to the end of the string, but what if you want a bunch of fields in the middle. Here are the Java/Scala equivalents there.
not that different, but the importing of MessageFormat and the use of a static method is orthogonal to the functionality we actually want. It adds nothing to the readability of the code, it's just more noise.
So, let's see where we are now. From the original Java we have:
And while we know a direct scala equivalent can be written as:
for (i <- 1 to 4) println ("Happy Birthday " + (if (i == 3) "Dear XXX" else "To You"))
We have actually ended up with:
(1 to 4).map { i => "Happy Birthday %s".format(if (i == 3) "Dear XXX" else "To You")) }
Suddenly that's starting to look cleaner, and it is probably more useful code for the world of web applications or rich GUIs, but the original aim was to print something else, so now we have the birthday song verse as a list of strings, let's just print them out:
birthdaySong.foreach { println (_) }
The _ is just shorthand for "the thing" (the only value available when iterating over the list). We could also have done:
birthdaySong.foreach { line => println (line) }
Perhaps the second is a little more readable, perhaps not after you get just a little more comfortable with the syntax.
So where does that leave things? Well, for the original scala answer, the one that prompted the complexity comment, we have:
(1 to 4).map { i => "Happy Birthday %s".format(if (i == 3) "Dear XXX" else "To You") }.foreach { println(_) }
Abandoning the need to keep it all on one line, we get:
val birthdaySong = (1 to 4).map { i => "Happy Birthday %s".format( if (i == 3) "Dear XXX" else "To You" ) }
birthdaySong.foreach { println ( _ ) }
In Java, even using String array as an intermediate, we get:
String[] birthdayArray = new String[4];
for (int i = 0; i < 4; i++) { birthdayArray[i] = MessageFormat.format("Happy Birthday {0}", i==2 ? "Dear XXX" : "To You"); }
for (String line : birthdayArray) { System.out.println(line); }
And for the regular old just print it out, we have (Scala first):
for (i <- 1 to 4) println ("Happy Birthday " + (if (i == 3) "Dear XXX" else "To You"))
and Java:
for (int i = 0; i < 4; i++) { System.out.println("Happy Birthday " + (i == 2 ? "Dear XXX" : "To You")); }
Can you still say Scala is more complex based on these examples? Is it not possible that it is just different?
Talk Back!
Have an opinion?
Readers have already posted
54
comments
about this weblog entry. Why not
add yours?
RSS Feed
If you'd like to be notified whenever Dick Wall adds a new entry to his weblog, subscribe to his RSS feed.
Dick Wall is a software engineer at Navigenics, Inc. a silicon valley startup that specializes in bringing genetic health information to people: http://navigenics.com - he also co-hosts the Java Posse podcast which covers news, interview and more in the Java community space: http://javaposse.com, and furthermore runs the Bay Area Scala Enthusiasts (BASE) user group: http://svscala.ning.com.