Unrolling Code Closures for Undergraduates

One consistent tendency I’ve noticed among undergraduate programmers is a persistent and incorrect belief that the number of lines of code is somehow tied to code efficiency. The thinking goes that the fewer lines they have, the faster the program will run. (Why they even care about speed at all is a story for another day.) For example, they get peeved when I write this:

int x;
int y;
int z;

instead of this:

int x, y, z;

The brighter ones may not be bothered by that, but this gets them all in a tizzy, nonetheless:

int low  = 1;
int high = 1;
for (int i = 0; i < 50; i++) {
  System.out.print(low);
  int temp = high;
  high = high + low;
  low = temp;
}

They want to see this

int low  = 1;
int high = 1;
int temp;
for (int i = 0; i < 50; i++) {
  System.out.print(low);
  temp = high;
  high = high + low;
  low = temp;
}

They love taking code out of a loop, even when the code in question (a declaration) doesn’t actually do anything; and they certainly don’t care if their resulting code is less readable. In fact, they sort of take it as a mark of honor if their code looks complex. They’re going to love closures.

I’m reading proposals like CICE and I see examples like this:

    new Thread(new Runnable() {
        public void run() {
            foo();
        }
    }).start();

and this:

new Thread(Runnable(){ foo(); }).start();

But both of these are bad examples! Both the closure and the non-closure syntax are more confusing and harder to read than they should be. I have to spend a lot of time puzzling over them and carefully decoding the interactions of the different parentheses and braces and periods and semicolons just to understand what either one is doing. Instead the non-closure syntax should be written like this:

Runnable r = new Runnable() {
        public void run() {
            foo();
        }
    };
Thread t = new Thread(r);
t.start();

In fact, I might even go so far as to move the runnable into a non-anonymous class so it’s even clearer what’s happening.

The more code you stuff into a line or into a single statement the harder the code is to read, and the harder it is to debug. The closure proposals seem to be all about stuffing more code into a line. I want less, not more. I want a syntax that more closely reflects what the compiler and interpreter are actually doing. That’s why I like using a separate line for each variable declaration, and it’s why I like named classes instead of anonymous classes.

One thing I don’t like about both of the extant proposals for closures (CICE and Gafter, et al) is that they hide what they’re doing. They pretend they’re giving us first class functions but what they’re really giving us are even more anonymous inner classes. This is the type erasure mistake all over again. The syntax is hiding what the compiler is really doing.

We may indeed need closures, but if we do they should be real first class functions, not fakes implemented with still more compiler wizardry. If this requires changes at the VM level, so be it. If the feature isn’t worth changing the VM over, then don’t implement it. It probably isn’t all that important anyway. But let’s do away with syntax that hides what’s really happening.

17 Responses to “Unrolling Code Closures for Undergraduates”

  1. Mike Says:

    “I want a syntax that more closely reflects what the compiler and interpreter are actually doing.” I want syntax that more closely reflects my mental model.

    I agree that both proposals end up shoving a whole lot of code onto one ugly line, and I don’t much like it. I don’t like C#’s closures either (are they technically closures? I don’t know). They’re all too verbose to be easily absorbed.

  2. John Says:

    As usual – a good post.

    I come across code all the time that is supposedly more optimized but is actually obfuscated.

    Simpler, cleaner code is much easier to understand and refactor

  3. Neal Gafter Says:

    You’re right: the closures proposals require the compiler hides the implementation techniques. That’s what’s bothered me all along with all high-level languages. If the machine is going to be executing machine code, we ought to have that machine code visible at the programming language level, right? There’s no point in hiding the mechanism behind a bunch of compiler trickery. Similarly with the so-called “if” statement: it turns into goto instructions. Well, by gosh, we might as well just use goto statements in the language!

  4. Doug Erickson Says:

    High information density requires a more powerful decoder and is more susceptible to transmission errors. It applies to your brain trying to interpret source code just as much as your HDTV trying to lock onto a weak channel.

    I’d rather spend my brain cycles on high-level design issues than trying to spot subtle changes in braces and parentheses that expand into radically different machine instructions.

  5. John DeHope Says:

    I find that more and more, I write fourth generation code like it was assembly! I do one statement per line. I break variable declaration and initialization into two lines. I get peevish when my functions are more than half a screen, and so I break them down into smaller units. I’ve gotten to the point where code duplication, even a single line, makes me search my brain for “what pattern is this?” Where did I read somebody say that if you’re writing code with more than 3 or 4 indentations, you’re doing something wrong?

  6. John DeHope Says:

    Closures can call me up when they look like this…

    // making all this stuff up, might as well be psuedocode
    Function f;
    f = new Function();
    f.ReturnType = new Type(“System.Void”);
    f.Arguments = null;
    f.Visibility = Function.Visibility.Public;
    f.CodeBody =
    {
    foo();
    }

    Runnable r;
    r = new Runnable();
    r.Function = f;

    Thread t;
    t = new Thread(r);
    t.start();

  7. Patrick Lioi Says:

    The C# 2.0 closures (yes, they do capture scope and are real closures) do have some cruft much like these java examples (what does the word “delegate” have to do with anything!?). The C# 3.0 lambda expressions syntax will help in most cases, though. It’s quite minimal in syntax and gets the intent across much better.

    I don’t equate this with the type erasure problem because generics are syntax sugar *with* particular expected behavior that gets wiped out by the Java implementation, while closures are syntax sugar whose expected behavior would *not* getting wiped out by the implementation. As long as the implementation does not contradict the expected behavior, then we basically shouldn’t care how it’s implemented (information hiding and all).

    You say “They pretend they’re giving us first class functions but what they’re really giving us are even more anonymous inner classes… they should be real first class functions, not fakes…”. I’m not sure what “real” could mean in this sentence :) First class functions == functions that are objects, which implies that first class functions behave like regular class instances. If they should behave like regular class instances, then regular classes are a perfectly appropriate implementation. As long as the intent is readable (as it is with C# 3 lambadas), then writing fewer lines is only a good thing.

  8. Slava Pestov Says:

    Why are you teaching Java to undergraduates? They will be hamstrung for the rest of their career. By the time they graduate Java will be mostly ‘legacy’ anyway.

  9. Stephen Colebourne Says:

    How a new language feature is implemented really shouldn’t matter to anyone but the compiler writer – it should be transparent. The problems occur when you get leaky abstractions, like generics. The task here and now is to ensure that closures has no leaky abstractions (IMO, BGGA closures does at the moment). Oh, and don’t forget FCM !

    BTW, John DeHope’s first reply is quite an eloquent way of pointing out why Java desperately needs closures (and why it should have had them earlier). Its that sense that we are have to write too much code to achieve our goal (simple, clear, easy to read later, logic). One response is to break everything down to one item per line. IMO, this isn’t the right response, but in the absence of closures it may be all we have. Its all explained better in the Kingdom of the Nouns – http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html. And I even agree with Slava in a way – teaching a language without closures no longer seems sensible to me.

  10. Fuel Says:

    pestov,
    Java will be mostly legacy??? on what basis do you state this ?? if your comment is based on the speed of java programs , then you are horribly wrong.

  11. Isaac Rivera Says:

    What’s wrong with a simple implementation like:

    Function f = new Function(String someArg, int anotherArg) {
    String result = someArg + “, ” + anotherArg;
    return result;
    };

    f.setAccess(Function.PUBLIC);

    We already use all of this syntax and it resembles anonymous class declarations. Its not unlinke closures on some scripting languages either.

  12. Isaac Rivera Says:

    Opps! did not escape my brackets! Here it goes again:

    What’s wrong with a simple implementation like:

    Function<String> f = new Function<String>(String someArg, int anotherArg) {
    String result = someArg + “, ” + anotherArg;
    return result;
    };

    f.setAccess(Function.PUBLIC);

    String s = f(“foo”, 2);

  13. Ricky Clarkson Says:

    Isaac,

    Neal goes through the reasons in this talk: http://video.google.co.uk/videoplay?docid=4051253555018153503

    By the way, there’s no use in making an anonymous function public/private. If you don’t want it to be called outside the current context, don’t give it out. When you make a StringBuffer, you don’t set an access level, so why would you do it for a Function?

  14. Ricky Clarkson Says:

    Elliotte,

    Stuffing as many ideas into one line of code as possible seems bad, true, but:
    Stuffing as many lines of code into one idea as possible also seems bad.

    “He can compress the most words into the smallest idea of any man I know.” — Abraham Lincoln, who was not a Java programmer.

    Good programmers can make a good decision about how many ideas per line of code are good. It’s not the language’s responsibility to make that decision for you.

  15. Tim Hanson Says:

    Higher level abstractions are good only when they don’t “leak”. This is exactly why the “if” statement is good. I don’t really care that it is implemented using gotos.

    This is also why implementing generics using type erasure is bad. I do have to know the implementation details. The closure spec seems to fall more into the latter camp.

  16. Isaac Rivera Says:

    Ricky,

    Agreed. I was trying to show how simple a previous post concern could be.

    However, “if you don’t want it to be called outside the current context, don’t give it out,” is not so simple. As you may want subclass or package privacy, no?

    Isaac

  17. börsenspiel Says:

    don’t equate this with the type erasure problem because generics are syntax sugar *with* particular expected behavior that gets wiped out by the Java implementation, while closures are syntax sugar whose expected behavior would *not* getting wiped out by the implementation. As long as the implementation does not contradict the expected behavior, then we basically shouldn’t care how it’s implemented (information hiding and all).

Leave a Reply