views:

328

answers:

4

Two years after does-the-jvm-prevent-tail-call-optimizations, there seems to be a prototype implementation and MLVM has listed the feature as "proto 80%" for some time now.

Is there no active interest from Sun's/Oracle's side in supporting tail calls or is it just that tail calls are "[...] fated to come in second place on every feature priority list [...]" as mentioned at the JVM Language Summit?

I would be really interested if someone has tested a MLVM build and could share some impressions of how well it works (if at all).

+3  A: 

Java is the least functional language you could possibly imagine (well, OK, perhaps not!) but this would be a great advantage for JVM languages, like Scala, which are.

My observations are that making the JVM a platform for other languages has never seemed to be at the top of the priority list for Sun and I guess, now for Oracle.

oxbow_lakes
Yeah! MUMPS ... I read about it. Unbelievable :-)
soc
Ever tried programming a Turing Machine?
Thorbjørn Ravn Andersen
@Thorbjørn - I wrote a program to predict whether any given program would halt in a finite amount of time. It took me *ages*!
oxbow_lakes
But did you finish?
Thorbjørn Ravn Andersen
The first BASICs I used didn't have functions, but rather GOSUB and RETURN. I don't think LOLCODE is very functional, either (and you can take that in two senses).
David Thornley
@David, functional != has functions.
Thorbjørn Ravn Andersen
@Thorbjørn Ravn Andersen: No, but it is kind of a prerequisite, wouldn't you say?
David Thornley
OP didn't use the word Java, just JVM.
Marco van de Voort
A: 

Tail call seems to be just a runtime optimization, and doesn't alter language semantics.

However it does change program behaviors: stack traces disappear; local objects may become unreachable prematurely (compared to normal method call). Neither violates Java langauge spec, yet they are very important features of Java platform.

I guess they have to be very careful before a backward compatible solution is found.

Tails calls are not that important for day to day Java programming, most Java code monkeys would never need it. And it is very simple to simulate tail call yourself by "trampoline" technique.

irreputable
TCO is not a runtime optimization!
oxbow_lakes
Tail calls are no more an optimization than garbage collection is. Would you consider GC merely an optimization over programs that allocate and just never free? TCO is exactly the same, but regarding the stack instead of the heap.
munificent
How is tail call NOT an optimization? It elides unnecessary instructions and maybe uses the stack more efficiently. The language semantics are unchanged.
Mr. Shiny and New
+4  A: 

Perhaps you know this already, but the feature is not as trivial as it may sound.

Consider the following program:

public class Test {

    public static String f() {
        String s = Math.random() > .5 ? f() : g();
        return s;
    }

    public static String g() {
        if (Math.random() > .9) {
            StackTraceElement[] ste = new Throwable().getStackTrace();
            return ste[ste.length / 2].getMethodName();
        }
        return f();
    }

    public static void main(String[] args) {
        System.out.println(f());
    }
}

Even though this has a "tail-call" it may not be optimized. (If it is optimized, it still requires book-keeping of the entire call-stack since the semantics of the program relies on it.)

Basically, the fact that the language exposes the call-stack makes it hard to implement this while still being backward compatible.

aioobe
Solution is easy. Keep a flag for functions that finger their stack (like g), and do not tailcall optimize any that call them.
Marco van de Voort
Found the mistake in your thought: "requires book-keeping of the entire call-stack since the semantics of the program relies on it". :-) It's like the new "suppressed Exceptions". Programs relying on such things are bound to break. In my opinion the behavior of the program is absolutely correct: Throwing away stack frames is the thing tail calls are all about.
soc
@Marco, but just about any method could throw an exception, from which the entire call-stack is bound to be available, right? Besides, you can't decide in advance which methods will indirectly call `g` in this case... think about polymorphism and reflection for instance.
aioobe
@soc, I'm not quite sure I follow you. What is suppressed exceptions?
aioobe
It is a side-effect caused by the addition of ARM in Java 7. It is an example that you can't rely on such things you showed above.
soc
@soc, Just because *some* new features breaks some backward-compatibility, doesn't mean that it doesn't make any difference if other features breaks backward compatibility too... No, there is no mistake in my answer. The fact that the stack-trace is exposed in the API poses a problem for (soft) tail-call recursion and is one (of many) reasons [why such feature is troublesome to add](http://blogs.sun.com/jrose/entry/tail_calls_in_the_vm).
aioobe
It does not break, because there is nothing to be broken. There is no promise that stacktraces don't change and they _do_ change from time to time (as I have shown above). Anyone relying on a certain stacktrace format is just wrong.
soc
Fortunately, Sun is not as lunatic as you guys are. Behavior comparability is very important. Language spec is not everything.
irreputable
@irreputable: Look at the facts before calling other people lunatic. *hint* ARM in Java 7.
soc
+2  A: 

Diagnosing Java Code: Improving the Performance of Your Java Code explains why the JVM does not support tail-call optimization.

But although it is well known how to automatically transform a tail-recursive function into a simple loop, the Java specification doesn't require that this transformation be made. Presumably, one reason it is not a requirement is that, in general, the transformation can't be made statically in an object-oriented language. Instead, the transformation from tail-recursive function to simple loop must be done dynamically by a JIT compiler.

It then gives an example of Java code that won't transform.

So, as the example in Listing 3 shows, we cannot expect static compilers to perform transformation of tail recursion on Java code while preserving the semantics of the language. Instead, we must rely on dynamic compilation by the JIT. Depending on the JVM, the JIT may or may not do this.

Then it gives a test you can use to figure out if your JIT does this.

Naturally, since this is an IBM paper, it includes a plug:

I ran this program with a couple of the Java SDKs, and the results were surprising. Running on Sun's Hotspot JVM for version 1.3 reveals that Hotspot doesn't perform the transformation. At default settings, the stack space is exhausted in less than a second on my machine. On the other hand, IBM's JVM for version 1.3 purrs along without a problem, indicating that it does transform the code in this way.

emory