Why Frege compiles to Java

Dierk König edited this page May 15, 2016 · 4 revisions

Frege compiles to Java source code, instead of directly emitting Java byte code. For that purpose we use a Java compiler (either the eclipse compiler in the Frege IDE, or the one that comes with the JDK). In any case, the java compilation is just a method call and runs in the current JVM, though we have also the possibility to run an extra process to invoke an arbitrary java compiler of your choice.

Note: in the following, we're referring to the software that does the java compilation for us as javac for simplicity. Again, that does not mean we need to fire up a process that runs javac.exe or some such.

We're often asked about the rationale behind this choice and here are some:

  • Frege follows the unix philosophy of composing tools that do their job well. javac compiles Java code well.

  • Calling javac introduces another level of robustness as it ensures the creation of valid byte code (assuming correctness of javac)

  • javac gives us an additional type check for free, as we strive for generation of warning-free fully generically typed Java code.

  • Java code can call other Java code and can be called from Java. Having the Java equivalent of your Frege code makes inter-operation much easier.

  • We follow the very successful approach of GHC that first compiles Haskell to intermediate languages. Java is one of our intermediate languages.

  • The Frege compiler becomes conceptually simpler as it is a Haskell-source to Java-source transformation.

  • The native interface can contain fragments of the java language like in:

      native availableProcessors "java.lang.Runtime.getRuntime().availableProcessors" :: () -> IO Int
    

    One would have to embed nothing less than a compiler for a subset of Java to ensure that java.lang.Runtime.getRuntime().availableProcessors() is a valid java expression that yields an int. The alternative would be to have a less flexible and convenient native interface.

  • The javac compiler is highly optimized and continuously improved for various platforms and JVMs. This is all work that we don't have to do.

  • Having the Java source code available opens all kinds of possibilities to use other compilers/transpilers like the ones for Java-to-JavaScript or Java-to-Dex (for Android).

  • The Frege compiler depends only on the JDK and nothing else.

  • The approach is future proof. For example, it is likely that radical changes proposed for the Java language like value types and primitive types as type parameters will not be possible without corresponding JVM changes. In the latter case, all what we'd have to do in the Frege backend would be to comment the code that is there to make sure that no primitive type ever appears as a type argument, and we're done and ready for the new era. In the former case, we could as well get away with doing nothing and still profit from the enhancements.

There are also quite some myths about this issue:

  • Some seem to assume that "manually" generated bytecode is somehow superior to the one generated by javac in terms of performance. The truth is that the JIT is the instance that makes a JVM program fast. Experience shows that byte code optimization doesn't contribute much, if any, to performance optimization. But even if this were so, the tools that do such optimizations can still be applied to byte code finally created from Frege source code.

  • There is the issue of byte code features that are not available in Java. This is all fine, it is only that none of those features would be of any use in the implementation of Frege.

    Even the famous invokedynamic instruction (which is quite important for JVM implementations of dynamically typed OO languages like Ruby) is not needed for statically typed non-OO languages like Frege where the majority of method calls go to static methods and the rest are calls to interface methods. You can't beat invokestatic with invokedynamic, it's as simple as that.

  • Finally, some argue that the Frege compiler could be faster when it would generate byte code right away. Despite there being no empirical evidence, it is indeed conceivable that this would be so. However, in practice, this would amount to a few fractions of a second per module at most. In exchange, the compiler would be more complicated and less easy to debug.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.