We don’t need anonymous inner classes? Bollocks to that.

Robert Fischer over at enfranchisedmind.com has a post about anonymous inner classes which I am forced to disagree with quite strongly.

He starts off with the example of a comparator, and shows that defining a lambda and then casting it to a Comparator is terser. I largely agree. I don’t agree with everything about that example, but it would be churlish to quibble over details. I concede the point: For one method anonymous inner classes, lambdas are better.

He then attempts to generalise this to anonymous inner classes with multiple methods.

Except he doesn’t really. He simply states that because Groovy lets you coerce maps to an interface, you don’t need anonymous inner classes.

This is an interesting point. Let’s take it further.

We can define the following function (in some sort of pseudo syntax because I don’t know Groovy):

defineClass("Kitten", {
  "meow" => () -> println("meow"),
  "play" => it -> ...
})

Hopefully it will be taken as uncontroversial that I don’t consider this to be the best idea I’ve ever heard. There’s certainly a place for things like this – e.g. if you’re starting from a prototype based language like Javascript and want to build a class system on top of it, something like the above makes sense. But with classes baked into the language, I much prefer

class Kitten{
   def meow = println("meow");
   def play(it) = ...
}

It’s cleaner and terser syntax, and it better expresses the meaning.

So. We now have two ways of expressing classes which map to our two ways of expressing anonymous inner classes. Except that for some reason we prefer one in the named class case and one in the anonymous inner class case. This is odd, to say the least.

Now I want to pick a bone with the claim that what we’re really doing when we create anonymous inner classes is passing multiple functions. It seems utterly contrary to reality. That may be true in some cases when you’re using them as an argument to some method, but most languages I know have this interesting feature called “returning values from functions”. I know you don’t see it getting used much what with the current fad for writing all your programs in continuation passing style, but it does happen occasionally.

Consider example the following implementation:

class Vector[T]{
   ...
   def elements = new Iterator[T]{
     var position = 0;
     def hasNext = position < length;
     def next = {val it = this(position); position += 1; it }
   }
}

This pretty unambiguously captures the intent: We’re creating a new iterator which captures the local scope. The alternative version (again, pseudo syntax):

class Vector[T]{
   ...
   def elements = {
     var position = 0;
     { 
       "hasNext" => () -> position < length;
        "next" => () -> {val it = this(position); position += 1; it }
     } as Iterator
   }
}

has absolutely no appeal to me. It muddies the intent and has worse syntax (if someone from the groovy world would like to tell me that the actual syntax for the above is better, please do, but I suspect I’ll still dislike even a slightly tider version of the above).

Iterators are one example, but there are plenty of others. e.g. in SBinary, formats have two methods read and write, and I return new ones as anonymous inner classes all the time. It would have been a maddening library to write without the capability to do that.

Anonymous inner classes get a bad reputation because so often their use is as a poor substitute for first class functions. But they really are a practically useful tool. I have plenty of other ideological reasons why I consider them vital to have, and this article was originally going to contain some of those, but I couldn’t be bothered. So I’m going to leave it at that: They’re useful, and they cleanly capture the intent in a way that I feel that coercing a map does not.

This entry was posted in programming on by .

6 thoughts on “We don’t need anonymous inner classes? Bollocks to that.

  1. Robert Fischer

    I stand by my statement: “Most anonymous inner classes are really closures at heart, the same way inversion of control is really monadic handling at heart.” The reason I do is because most anonymous inner classes are of the one-method-interface-impl variety or its sibling, the one-method-abstract-impl variety. And these are first-class functions in OO clothing: “closures at heart”.

    I don’t say all uses of anonymous inner classes are really closures at heart. Although I argue that if you’re doing any kind of heavy lifting logic (and your iterator well counts), then you shouldn’t be dealing with a named class, not an anonymous one, and therefore Groovy still doesn’t want/need anonymous inner classes. An inner class certainly *is* useful if you want to feed inner state to an object, although Groovy’s flagrant disregard for permissions makes that argument for inner classes a lot weaker.

    BTW, this:
    def elements = {
    var position = 0;
    {
    “hasNext” => () -> position () -> {val it = this(position); position += 1; it }
    } as Iterator
    }

    is:
    def elements() {
    def position = 0
    return [
    hasNext: {-> position
    def toReturn = this[position]
    position += 1
    toReturn
    }
    ] as Iterator
    }

    Although not awesome, I’ll take that elements impl over this one any day:

    def elements() {
    return new Interface() {
    def position = 0

    boolean hasNext() { positon < length }

    def next() {
    def toReturn = this[position]
    position += 1
    toReturn
    }
    }
    }

    The difference is really in the way the language is spoken: anonymous inner classes are really going to be obviously foreign in Groovy, because it doesn’t flow with the rest of the language. Moving the class information from the first thing you’re reading to basically tacking it onto the end makes sense in the dynamic context of Groovy. But this wouldn’t make sense in — say — Scala.

  2. david Post author

    “most anonymous inner classes are of the one-method-interface-impl variety or its sibling, the one-method-abstract-impl variety” is only true in languages which don’t support any sort of lambda construct. The whole point of the example I gave is that there are plenty of examples where anonymous inner classes continue to get used that *don’t* fit this pattern.

    As to the notion that one should use named classes, this seems… well, not so much an argument as an assertion. And it’s one that doesn’t make any sense to me at all.

    Consider the options: I have a class that I want to instantiate exactly once. I can either a) Not name it, put it exactly where it is used and just let it capture the local scope or b) Assign it a meaningless name, figure out somewhere appropriate to put it and manually write code to transfer the state from the local scope over to it. (In the iterator case it doesn’t matter that much because elements doesn’t capture any arguments. But when you want to capture local variables or arguments it quickly gets out of hand).

    So, I basically consider “use a named class” not a viable option.

    So it comes down to the question of coercing maps vs. instantiating classes. And I simply don’t see the appeal of the map option. It seems unnecessarily magic. Further it doesn’t transition smoothly to more general uses – you end up with a sharp discontinuity between the map of functions syntax and falling back to using a named class. The anonymous inner class plays well with the class and object system, so doesn’t fall down when you hit areas which the magic doesn’t support (I have no idea exactly what these are because I don’t use Groovy. Does Groovy allow interfaces with implementations? Does it have an independent mixin mechanism? If not, can this trick work when returning a class?)

    I also don’t understand your point about syntax. I assume groovy writes constructors the Java way: new Foo( ). Thus it seems to make sense to construct other classes in exactly the same way. You could make a distinction for interfaces, but why bother? Doing things one way for classes and one for interfaces seems needlessly confusing.

    Finally, I want to take terminological issue with your “Most anonymous inner classes are really closures at heart”. I know what you really mean is “functions”, but actually *all* anonymous inner classes are closures, not at heart but in fact. They capture their defining scope in exactly the same way that a “closure” does, and are implemented the same way.

  3. Robert Fischer

    To summarize your argument: you’re not really with the Groovy syntax. That’s fine — you don’t write Groovy, so I wouldn’t expect you to be. In fact, coming from Scala, it’s totally unsurprising to me that you don’t see value in a succinct syntax.

    There’s a lot of places where Groovy does not use “new Foo()” to construct objects. Inline lists, maps, anonymous function, and “as” coercions are far and away the most popular choices. So it’s kinda annoying to have to fall out of succinct code and back to something as chatty as:
    new Foo() {
    void bar() { … }
    void baz() { … }
    }

    Plus, the @Newify AST transform pushes the distance between Groovy and Java’s “new Foo()” syntax yet further. The whole language is trending away from that stuff.

    And yes, programmers conflate “closure” with “anonymous function”. Get used to it — the chance to stop that terminological issue has long past. :P

  4. david Post author

    I’m more than a little offended by the complete reality disconnect demonstrated by “In fact, coming from Scala, it’s totally unsurprising to me that you don’t see value in a succinct syntax”. The problem in fact seems to be that method declarations in groovy are needlessly verbose: The syntax you’ve demonstrated is *not* substantially more succinct than Scala anonymous inner classes.

  5. david Post author

    Also: I have gotten used to it, and I am happy to accept the misuse of people referring to anonymous functions as closures. It’s a terminological abuse, but I can live with that.

    But when you refer to other things which actually *are* closures in ways that suggests that they’re not closures because they’re not anonymous functions, you’re simply wrong and I will correct you.

Comments are closed.