Java Code compared to Scala Code (3) - Turning List<Foo> into List<Bar>

Feb 16, 2011 12:58 · 878 words · 5 minutes read Scala Java

I just had to write a piece of Java code that made me miss Scala again. My current private project requires to do MIDI I/O. For that I need to display the names of installed MIDI devices in a dropdown box so the user can choose one. I have a convenience class that deals with the Java MIDI API, to which I want to add a method that returns the names of all installed output devices. The details of the MIDI API do not matter here. I already have a list of output devices, all I need to do is build a list of device names from it.

The Original in Java

public List<String> getOutputDeviceNames() {
    ArrayList<String> result = new ArrayList<String>();
    for (MidiDevice md:outputs) {
        result.add(md.getDeviceInfo().getName());
    }
    return result;
}

The Scala version

def outputDeviceNames() = outputs.map(_.getDeviceInfo().getName)

My 2 ¢

I do not know how many times in the last 10 years I have written Java code like the above. Again, do you really believe the Java version is easier?

For the Scala newbies coming from Java, you might wonder how this can work, so here it is:

Where is the loop?

It is hidden in the map function. This is one of the things that seems to scare people that do not have done functional programming before: map is a function that takes a function as an argument. If you do not like the idea, just imagine the following pseudo-Java:

public interface ApplyToElement<S,T> {
    public S apply(T element);
}

//in ArrayList.java:
public <T> ArrayList<T> map(ApplyToElement<S,T> f) {
    ArrayList<T> result = new ArrayList<T>(this.size());
    for (S s:this.elements) {
        result.add(f.apply(s))
    }
    return result;
}
//now this is how you would call our "Java-map"
public List<String> getOutputDeviceNames() {
    return outputs.map( new ApplyToElement<MidiDevice, String>() {
         public String apply(MidiDevice element) {
             return element.getDeviceInfo().getName()
         }
    }
    );
}

Of course we do not do that in Java, as it is just as verbose and not better to understand than the first version with the explicit loop. Scala allows us a much neater syntax to create an instance of the above interface (because that’s what a function is: an anonymous class implementing an interface with just one function: apply).

It takes some getting used to if you have never done anything like it before, but it is not rocket science. Try it, and I promise, after a few days you will wonder what all the fuss with “functions as first class members” is about. It is just another object you pass around. (Or for the C-guys: just a function pointer, whichever pleases you)

What’s with the _?

The underscore is useful syntactic sugar when defining a function. It simply means “use the first variable here”. Let’s look at the verbose version of the above code and simplify it step by step:

outputs.map((md:MidiDevice) => md.getDeviceInfo().getName)

The arrow => means it is a function taking the left side as argument and returning the right side. The Scala compiler is much cleverer than the Java compiler however, so it can figure out by itself that we are talking about MidiDevices (how it does so is explained below), so we can leave that bit out:

outputs.map(md => md.getDeviceInfo().getName)

We only use the variable once however, so why bother even giving it a name? This is what _ does: The compiler knows the argument to map needs to be a function with one parameter, so it treats the whole stuff in braces like a function with one Parameter. It sees the _ but no arrow, so it just plugs the first variable into the _.

Where is the return?

Scala has no return, because it does not need one: every statement has a value. The last value in a method is returned. Take this example:

//method that adds 42
def add42(x:Int) = {
    println("got " + x)
    //no return necessary, it is the last
    //statement in the function, so it is 
    //returned 
    x + 42
}

Even statements that have no value have a value: It is called Unit and has exactly one Instance. To every Java programmer whining this Unit thing is too complicated:

What do you think void means!? You typed it a gazillion times. It was not so hard then, was it?

Where are the return types?

The Scala compiler knows:

  • outputs has type List[MidiDevice].
  • map turns a List of As into a list of Bs
  • so A in this case means MidiDevice
  • getName returns a String
  • so B in this case means String
  • the result of map is a List[B]
  • that means the overall result of the line is List[String]
  • it is the last line in the function, so it is the return value
  • so the return value of the function is List[String]

It is called logic reasoning, something which computers are particularly good at. This means that writing a Scala compiler is much harder than writing a Java compiler. But luckily someone has done that for us already.

Again, if you think that is complicated:

Every time you write a Java function, you do the same reasoning. Now it is done for you. So you have to do less work which means your work is more complicated and harder? Sorry, but there is an old robot saying for this kind of reasoning: DOES NOT COMPUTE!