Wednesday, January 28, 2009

Deeper Look at the Apply Method in Scala

In Scala, there is a language feature generally referred to as "The Apply Method" that has the following rules:


  1. Any object that has an apply method can be called with the .apply omitted.
  2. Functions are no more than objects.


Let's take a look at an example. Given the following abbreviated definition of class Array, and an instance a,

class Array{
def get(index:Int) = { ...some code to get from the array... }
def apply(index:Int) = get(index)
}

val a = new Array(whatever)

Then the following calls are essentially equivalent:

a.apply(7)
a(7)
and also:

a.get(7)

(the call to get is only equivalent because apply simply calls get. apply could have any implementation, of course.)

While apply is really useful in cleaning up syntax, the real beauty of it is hidden just below the surface. (Note: Some of the following might not be implemented exactly as I describe, I'm not 100% sure, but for the sake of understanding, I think it's okay.)

Scala'a apply is a concept that can be considered universally across functions or methods, objects, anonymous functions, case classes, and the like. In Scala, a method is nothing more than a Function object containing an apply method which takes the same arguments as the method itself.

Let's take a look at a more in depth example. All three of the following are basically equivalent:
  
def shout1( word: String ) = println(word + "!")

val shout2 = new Function1[String, Unit] {
def apply(word: String) = println(word + "!")
}

val shout3 = (word:String) => println(word + "!")

All three are of type Function1[String,Unit], which has an apply method that takes a String and returns Unit. Notice that shout3 uses anonymous function syntax. This is really nothing more than syntactic sugar for Function1. All three can be called like so:

shout1("hey")
shout2("hey")
shout3("hey")

The following client code further demonstrates that they all derive from the same type.

def ohYeah( f: String => Unit ){
f.apply("oh yeah")
f apply "oh yeah"
f("oh yeah")
}

ohYeah(shout1)
ohYeah(shout2)
ohYeah(shout3)

However, one thing I find odd that cannot be done is the following:

shout1.apply("hey") // compiler error!
shout2.apply("hey") // ok
shout3.apply("hey") // ok

If anyone smarter than me cares to chime in on that one, I'd be quite happy. Apparently Scala doesn't always treat actual methods exactly the same as the equivalent Function values, but I'm not sure why. I tend to think it should. There must be a good reason.

The first rule applies to top level objects, functions, and objects defining apply, but what about classes? Are classes no more than objects in the language, the same way that functions are?

Unfortunately, it appears the answer is No.

Before we get into that, let's that a quick look at the exception to that - case classes. For any case class C, you can say C(), and that returns you an instance of that class. Hmm... C(). That looks an awful lot like what we've been looking at. As it turns out, it is.

In Scala, when you define a case class, a corresponding top level object with the same name is created. This object is a factory which creates objects of the original class. It contains an apply method that takes the same arguments as the constructor of the original class and simply calls that constructor.

A quick example should help:

case class Dood(name: String)

class DoodClient {
val a = new Dood("Jack")
val b = Dood("Daniels")
val c = Dood.apply("Cough")
}

The expressions in b and c are equivalent. b is just shorthand for c. In both lines, Dood refers to the top level object Dood, and not the class.

Knowing all this, I should correct myself. I said case classes are an exception to the rule that classes aren't simply objects in the language. This isn't really true. Creating the case class Dood creates both a class and a top level object. It's that object that has the apply, and the class itself still isn't a full fledged object.

But why? Well, I think the short answer is that on the JVM classes aren't top level objects. That seems like a cop out answer though. Let me explore it for a bit, though I'm really just thinking out loud.

If they were top level objects containing an apply method then we could probably do away with the new keyword entirely, and then any call to any object would simply be a call to apply. Things would be entirely consistent both from the internal language representation, and from the perspective of the developer as well.

But what are the consequences of this? I guess I'm not sure. I'm going to have to think through it a bit harder. I'd appreciate comments from anyone with more knowledge on the subject than myself.

Monday, January 26, 2009

Maven: WTF

I'm on the train, trying to do a build, and this happens:

[INFO] ------------------------------------------------------------------------
[INFO] Building myProject
[INFO] task-segment: [clean]
[INFO] ------------------------------------------------------------------------
[INFO] artifact org.scala-tools:maven-scala-plugin: checking for updates from scala-tools.org
[WARNING] repository metadata for: 'artifact org.scala-tools:maven-scala-plugin' could not be retrieved from repository: scala-tools.org due to an error: Error transferring file
[INFO] Repository 'scala-tools.org' will be blacklisted


WTF?

Because I'm not online maven totally explodes and threatens to blacklist stuff?

I'm sure this is 'configurable' or whatever...but wtf, this just shouldn't happen. Since I wasn't online, I couldn't easily lookup what to do, so I couldn't build at all. Thanks Maven.

I'm thoroughly annoyed.