Log in

No account? Create an account
eyes black and white

Programming on valium

Google AutoValue: what in Lisp would take a few hundred lines max in Java is over 10000 lines not counting many, many libraries. Just WOW!

Thus, Java has macros too, it's just that they are 10 to 100 times more programmer-intensive than Lisp macros. I feel like I'm back in the dark ages.

Even for "normal" programming without new macros, a program I wrote both in Java and in Clojure was about 4 times bigger in Java (and that's despite using AutoValue). I also took ten times longer to write and debug the Java program (despite having written the Clojure program before, so no hard thinking whatsoever needed), with a frustrating edit-compile-run cycle many orders of magnitude slower. Part of the difference is my being much more experienced in Lisp than in Java, but even accounting for that, Java is slower to develop with.

The Java code is also much harder to read, because you have to wade through a lot of bureaucracy — each line does less, and so may be slightly faster to read, yet takes no less time to write, debug, modify, test, because of all the details that need be just right. Yet you must read and write more Java, and it's therefore harder to get the big picture, because there is less information available by screenful (or mindful) and much more noise. The limitation on available information is not just per screenful but also per file, and you find you have to jump constantly through so many files in addition to classes within a file; this is a lot of pain, even after accounting for the programming environments that alleviate the pain somewhat. Thus the very slight micro-level advantage of Java in readability per line is actually a big macro-level handicap in overall program readability.

Lack of both type aliasing and retroactive implementation of interfaces also means that type abstraction, while possible with generics and interfaces (themselves very verbose, though no more than the rest of the language), will require explicit wrappers with an immense amount of boilerplate, if not reimplementation. This strongly encourages programmers to eschew type abstraction, leading to more code explosion and much decreased maintainability.

Also, because function definition is so syntactically cumbersome in Java, programs tend to rely instead on big functions with a lot of side-effects, which yields spaghetti code that is very hard to read, understand, debug, test or modify — as compared to writing small conceptually simple functions that you compose into larger ones, as you would in a functional programming language.

The lack of tuple types is also a big factor against functional programming in Java: you'll need to declare a lot of extra classes or interfaces as bureaucracy just because you want a couple functions to pass and return a few values together (some people instead use side-effects for that — yuck). You could use a generic pair, but that leads to horrible types with many<layers<of<angle,brackets>>> which is very hard to read or write, and doesn't scale to larger tuples; of course, the need to declare types everywhere instead of having them inferred by the compiler means that even with tuples of arbitrary size, you'll need to spell out long unwieldy types more often that you'd like. Ignorants complain about the number of parentheses in Lisp, but just because of the size increase, there are a lot more parentheses in my Java program than in my Lisp program, and if we are to include all curly, angle and square brackets, that will be another many-fold increase.

Java 8 makes the syntax for functional programs slightly easier, and AutoValue makes it slightly less painful to bundle values together, but even with these improvements, Java remains extremely verbose.

The standard library is horrible, with side-effects everywhere, and a relatively poor set of primitives. This leads to the ugly habit of having to resort to "friend" classes with lots of static methods, which leads to a very different style of invocation and forces more bureaucratic wrapping to give things a unified interface. The lack of either CLOS-style generic functions or Clojure-type protocols mean you can't add decent interfaces to existing data-structures after the fact, making inter-operation with other people's code harder, whether you decide to adopt your own data-structure library (e.g. a pure functional one) or just try to extend existing ones. Lack of multiple inheritance also means you have to repetitively repeat a lot of boilerplate that could have been shared with a common mixin (aka trait class).

All in all, Java is just as heavily bureaucratic as I expected. It was developed by bureaucrats for bureaucrats, mediocre people who think they are productive when they have written a lot of code for a small result, when better tools allow better people to write a small amount of code for a big result. By analogy with programming languages said to be a variant of something "on steroids", I'd say that Java is a semi-decent programming language on valium. As to what template is sedated, I'd say a mutt of Pascal and Smalltalk. But at least it's semi-decent, and you can see that a lot intelligent people who understand programming language design and implementation have worked on it and tried to improve upon the joke of a language that Java was initially. Despite the bureaucracy, the sheer amount of talent thrown at the language has resulted in something that manages to not be bad.

This hard work by clever people makes Java so much better than Python, an attractive nuisance with lots of cool features that lead you into a death by a thousand cuts of small bad decisions that amplify each other. Superficially, Python looks like a crippled Lisp without macros and with a nice toy object system — but despite a lot of very cool features and a syntax that you can tell was spent a lot of time on (yet still ended up with many bad choices), Python was obviously written by someone who doesn't have a remote clue about semantics, resulting in a lot of pitfalls for programmers to avoid (there again with side-effects galore), and an intrinsically slow implementation that requires a lot of compile-time cleverness and runtime bureaucracy to improve upon.

In conclusion, I'd say that Java is a uniformly mediocre language that will drag you down with bureaucracy, which makes it rank well above a lot of overall bad languages like Python — but that's a very low bar.

Does this rampant mediocrity affect all industries? I'm convinced it does — it's not like these industries are fielded by better people than the software industry. Therefore it's an ever renewed wonder to me to see that the world keeps turning, that civilization endures. "A common man marvels at uncommon things; a wise man marvels at the commonplace." — Confucius



We have at least 50 years experience telling us that programming in languages freer of odd side effects and syntax is more effective than using languages with obtuse, overblown syntax and cranky semantics. Yet fools continue to design and promote them, often using wild explanations to attempt to promote their flaws as features (Bjarne Stroustrop and C++ have elevated this to an art form, in whose footsteps I see Guido Van Rossum following). And our industry continues to use them. I despair of any change other than that of my own habits. I'm very happy with my Lisp-based languages, thank you.

"Java is a DSL for taking large XML files and converting them to stack traces"

The Perils of Partially Powered Languages disses the combination of Java with external DSLs like XSLT or Ant, in favor of internal languages such as Haskell's xml-hamlet. I've discussed this case before. To me, not only XSLT or Ant are partially powered languages — so is Java.


Sometimes, you really want Memoization, but while in Lisp it's a trivial hundred-odd lines of heavily-commented code (as in my fare-memoization), it's a lot of code in Java, that doesn't quite automate as much (see, e.g., Guava's CacheBuilder or Interner), because not only there are no macros, but there is no easy tuple type to represent the arguments and you have to declare a new value type (hopefully using AutoValue) for every new function signature for which you want to memoize a function. Ouch. Time to add AutoMemoize to the Google auto library?

Side-effects combined with high-costs of encapsulation

Because functional programming is expensive (the simplest function takes fives lines instead of 20 characters), and because macros are unaffordable (see AutoValue above), most people eschew encapsulation of side-effects within higher-order functions or macros, and instead manually apply "design patterns", becoming human compilers. Of course, humans are bad compilers and this is the source of a lot of bugs, ever repeated, that would otherwise have long been encapsulated and automated away. When in addition, these manually-macro-expanded things include side-effects, and especially so in the presence of concurrency, debugging and maintenance become nightmares.

And Java programmers think this is "normal".

Java Shop Politics

Shriram K linked to this post on the social effects of Java:


During my recent year into Haskell, I have noticed how much liberating lazy evaluation is, in general, and how much of the need in macros it does remove.

At least, I've yet to face an immediate need to employ Template Haskell.

Sometimes, though, I can't help but wonder, how much easier would it have been, if the various higher-level constructs in Haskell had a moral equivalent of documentation as to how they expand into Core (the simple lambda calculus which everything expands to), and if such "macroexpansion" was available to programmers.

Perhaps it would be too volatile, since type system research never ceases in the lands of GHC..
I think any think good project need more patience
eyes black and white

October 2017



Powered by LiveJournal.com