Sunday, July 20, 2008

A Lisper's initial experience with Java

I started a new job a little over a week ago wherein I have to write Java code, mostly writing tests in JUnit.  I will state up front that I do not have anything against Java.  I think it's a decently designed language considering when it was designed and its target audience.  I should also say that I'm impressed with Eclipse. I doubt my experience with Java would be as enjoyable without it. Kudos to the Eclipse team. That said, I sorely miss the qualities of Lisp when trying to do my work.

Now, I haven't done extensive work with Java yet since I am still getting up to speed on many things at my new place of employment.  Thus, it may be the case I don't know how to do certain things effectively in Java/JUnit.  Still, here are some things I really miss in Lisp when moving to Java.

Special variables: Debugging in the system I'm using involves changing various static constants in classes.  This is fine if you have write access to things, but when you don't or when obtaining it may cause conflicts with the version control software, it's a pain.  I do not like that it is a compile-time constant.  (let ((*debug* t)) ...) would be so much nicer.

CLOS and its MOP: You don't realize the power of generic functions until you don't have them.  Some of the code I'm working on is old and contains, shall we say, questionable design decisions.  (To be fair, the old code would never pass current code reviews, but it's now production code, so we can't change it.)  Testing it is problematic because I have to extend existing (mock!) classes to get at the objects I need to examine at the right time.  Adding an around method to a particular method would be much cleaner.  Even better, adjusting the methods programatically while debugging would be great.

Multiple inheritance: The desire to use multiple inheritance in Java may pass as I get more accustomed to Java, but certain common methods would be better served if implemented as a mixin or something similar.  Right now, there's more code repetition than there should be, in my opinion.  However, it can't be factored out into separate classes very easily.

Overall, what I dislike about using Java is the inability to poke and prod a running system to learn something about how it works.  Sure, you can use a debugger with breakpoints, but when an error occurs, I can't drop down to Java code and start making calls. It's the interaction that's lovely about Lisp and Java just doesn't have it in the same way. Partial information is powerful. Interacting with a failure is powerful. Being able to navigate the stack after an exception has been thrown while the system is still loaded is powerful. They are powerful because they provide information that may otherwise be hard to obtain. Instead of tracing through code, I can interact with data. The latter is considerably more productive, in my experience.

13 comments:

Erik R. said...

As a Java programmer first and a Lisper second, I find your take on my principle language very interesting.

Personally, I've never liked Eclipse, and went directly from NetBeans to IntelliJ IDEA when IDEA first became viable (~v2.0), so I cannot speak for Eclipse's debugging abilities. But, regarding your last paragraph, in IntelliJ IDEA, it is very possible to break on an exception and evaluate full method calls at any level of the stack while debugging. And, as long as your change isn't to the class that your breakpoint is in and don't change any method erasures (i.e. adding or removing parameters), you can even make changes and compile them directly into the paused JVM during debugging.

I understand your frustration with relation to special variables and mixins. For the mixin bit, I've developed a partial solution via interfaces and inner classes that is pretty lame by Lisp standards, but works fairly well given the restrictions of Java.

Again, great post. Very interesting.

Richard G. said...

I've used a lot of languages and the lack of mixins in Java is vexing. I understand the dangers of multiple inheritance but the limitations of interfaces often results in the same functionality being implemented multiple times, which is a risk of a different nature.

Ruby provides a fairly simple and powerful way to handle mixins. It would be nice to see Java adopt this.

Other options to solve the issue start to look like Rube Goldberg machines. E.g., C-style preprocessors hooked into the build system, using Java to compile and link the module at run-time, etc.

Anonymous said...

use javax.script.*. Write your code in JavaScript or somesuch and have it run on the JVM.

Steve Knight said...

Like Erik. R, I have come the opposite direction to you learning Java (8 years ago) and then Lisp about a year ago. These insights of yours are interesting because if I adopted them they would make me write better Lisp-as-Lisp code, instead of Java-as-Lisp. If that makes sense.

Post more insights like this when you have time!

Steve

Daniel Martin said...

It is indeed possible to evaluate code while the debugger is stopped at a breakpoint in eclipse, but how to do it isn't immediately obvious, nor is there a nice REPL prompt. (unless that's provided by a plugin I'm not aware of)

To evaluate code in vanilla eclipse, you must stop your code at a breakpoint (just hitting the pause button while your thread is waiting on synchronization won't do it) and then open the "Display" window. (Find it in the "windows" menu, if it isn't already open in your debugger) Then, just type some java code into the display window, highlight it, and click the button in the upper right corner of the window to run the selected code and show you the result. (There are two buttons - one runs and displays the result, the other just runs the code)

Be aware that using System.out or System.err will dump stuff to the "Console" window, not the "Display" window where you're typing code. Code will be evaluated in the context of the currently selected stack frame.

ivant said...

I think AspectJ provides around methods and more.

Ricardo said...

Hi,

Perhaps you should take a look at Groovy. It supports metaprogramming, closures and many other great toys you might be used to. Even if you can't write your code in Java, the Groovy shell is great for exploratory programming.

Anonymous said...

My bad, that should have been "even if you can't write your final code in Groovy", not Java.

Anonymous said...

Wait until you spend at least 2-3 months with Java. I thought as well that Java is "decent", but I now hate the language completely. And Eclipse suck badly - I got into situation that it's "compile in background" completely messed up the class are everything was looking ok except some heisenbugs which started to appear (and yes, cleaning and rebuilding did not work since Eclipse stored it's classes elsewhere). Last time I wanted to debug a class, constructors of static variables did not get called! The retarted generic types schizophreny is a unique example of idiotic design. Not to mention sometimes 10 minutes time to start up Tomcat, 1 hour time to import little of content to crappy Magnolia on P4 3ghz, 4gb ram. Java sucks fucking horribly. Getting back to SLIME's REPL is such a relief. If you think you are smart because you are playing with clever macros and walk your code like champ, wait until you spend a !!week!! on something so trivial you would write it in 2 hours in 10 times less space and correctly. And yes, Java has lot *buggy and broken* libraries, to blow in your face. Good luck!

sanity said...

It probably isn't an option for your employment, but you should take a look at Scala.

It compiles to straight Java bytecode, and interacts seamlessly with Java. It also supports closures, mixins, and many other more powerful language features that Java lacks.

It also does type inference, which removes a lot of the verbosity that is common in statically typed languages, while retaining the compile-time type safety.

There is an eclipse plugin for it with neat features like code completion, but its still at an alpha stage of development.

Anonymous said...

In addition to Scala, you might also want to look at Groovy, Clojure (Lisp like) and Jython. It's relatively easy to create a console window that accepts input from these languages and attach the console to what you want to run - thus getting runtime poke/peek.

Anonymous said...

To the anonymous who claims Java has a lot of broken and buggy libraries, please do report anything you find!

I am a Java programmer firstly, and Lisp programmer secondly, and I have only once come across a place where I really wanted multiple inheritance.

And I really love Eclipse. I can't see how I could have done my work without it. I have been using it extensively for over a year on a daily basis (8+ hours a day) and it really is a great piece of IDE.

JS said...

Hello Geoff,

You should take a look at Clojure and AspectJ. Clojure is a Lisp that's well-integrated into the Java Virtual Machine and is fairly stable now even though it isn't that old. AspectJ is the implementation of Aspect-oriented programming. Gregor Kiczales, who also was involved with the design of CLOS, is responsible for AspectJ and its purpose was to bring Java closer to LISP. These are your two best bets if they are viable Java-based options at your work for reducing the code you have to write. Other than these, you might have to resort to writing a Lisp -> Java compiler or otherwise generate Java code in another language.