
Why I love Common Lisp and hate Java (2012) - pmoriarty
https://kuomarc.wordpress.com/2012/01/27/why-i-love-common-lisp-and-hate-java/
======
nayuki
It is a recurring theme for people to point out how verbose a hello world
program is in Java:

    
    
        class HelloWorldApp {
            public static void main(String[] args) {
                System.out.println("Hello World!");
            }
        }
    

For argument sake, let's compare it not to Lisp but to Python instead:

    
    
        print("Hello World!")
    

Once you get proficient in a language and start writing larger programs, your
perspective can change entirely. What appears to be verbose in the Java
version starts to make sense. For example:

* In Java the top level only contains classes and interfaces. All functions must be put inside a class, and I believe this design simplifies conceptual understanding. Whereas in Python, you have a distinction between top-level variables and functions versus fields and methods bound in classes.

* Python always executes the top level, so you end up with awkward idioms like: if __name__ == "__main__": main(sys.argv)

* Static typing versus dynamic typing. We all know this debate, so I won't repeat it. (This is regarding void and String[].)

* The "public" keyword might seem like noise, but in Python you denote private by prefixing the name with one or two underscores.

* Do you really want print() to be so easily accessible in the top-level namespace? It seems to me that the bigger an application gets, the worse it is to easily litter the codebase with print statements.

* You still eventually need to learn what "@staticmethod" means in Python. Java just forces this learning early on.

~~~
rococode
Totally agree with you - I like Java precisely because it's so verbose. I'd
rather have to type 20% more than spend 20% more time figuring out other
people's code.

And honestly, I always feel that the Hello World example is kind of biased.
It's more an example of Java's way of launching a program being verbose
because every program requires a certain minimal structure. But for the rest
of the entire Java language that's not the main method, the only real
"verbosity" comes from having to declare types, which is pretty much just a
requirement of statically typed language and isn't so specific to Java itself.

I've written reasonably large amounts of code in Python, Javascript, Ruby,
Racket, StandardML, etc. Every language has its own little forms of "beauty"
\- chunks of code that are particularly short and pretty to write in those
languages. And I do get a certain amount of satisfaction doing trickier
operations in a couple lines in languages like Python. But I always end up
preferring Java's more verbose style because I don't want to have to think
about the correct syntax to maximize my code's stylishness.

~~~
nayuki
I would say that Java has ~50% more code than Python due to types and braces.
But I agree with you that it's worth it, because the compiler can check that a
programmer satisfied a constraint, instead of relying on informal conventions
or comments.

You can have a statically typed language without verbosity, via type inference
(Haskell, newer Java, etc.). I wouldn't say I enjoy Java's verbosity, but I
would take this over shooting myself in the foot because Python does little to
protect me from my own silly type/name mistakes.

I do lament that Java falls short on being able to declare tuples, lists, and
dictionaries easily.

~~~
ilovecaching
Most Haskellers write out the types even though they can be inferred. In a
pure language, signatures are extremely close to documentation (you know
exactly what a function will do by its signature).

Java requires more code than Python or Haskell because it's extremely
imperative and statement based. Python on the other hand includes a lot of
functional, more expressive idioms like concise list comprehensions.

~~~
FranzFerdiNaN
Types aren’t documentation, no matter how many Haskell users delude themselves
thinking it is.

Documentation is more than a function definition. Types don’t explain
rationales and how to use functions and programs. Types don’t give proper
examples.

~~~
chowells
Types are really good documentation. I can tell I'm not deluded by thinking so
because I can sit down with a Haskell library containing zero examples and
_write code that uses it_. If your assertion was correct, that would be
impossible. But it's not only possible, it's easy. Perhaps there is more to
types than you realize.

~~~
pmoriarty
Alice: Hey Bob, want to try my nifty new function?

Bob: Sure, Alice, what does it do?

Alice: It takes an object and it returns an object.

Bob: But what does it actually do?

Alice: I told you. It takes an object and it returns an object.

Bob: But what does it do with the object? What's it for? Why should I use it?

Alice: Hey, I just gave you the full documentation for my function. You have
everything you need. Now go use it.

Bob: Um, no thanks. I like to know more about what I'm getting myself in to.

~~~
xboxnolifes
Thankfully, Haskell supports much nicer types than only `Object`

~~~
pvorb
Alice: Hey Bob, want to try my nifty new function?

Bob: Sure, Alice, what does it do?

Alice: It takes a string and it returns an integer.

Bob: But what does it actually do?

Alice: I told you. It takes a string and it returns an integer.

Bob: But what does it do with the string? What's it for? Why should I use it?

Alice: Hey, I just gave you the full documentation for my function. You have
everything you need. Now go use it.

Bob: Um, no thanks. I like to know more about what I'm getting myself in to.

~~~
ilovecaching
I think you're criticizing a language you have no experience in. Haskell
development is type driven. You start by defining and constraining your types
until you get a DSL to write signatures in. See the Idris O'reilly book for
more on type driven development.

By the time you've added type constrains through typeclasses, selected types
named after the domain, and selected a suitable name for the function, it's
possible to write against the signature with no knowledge of the body or
comments.

You're also forgetting that signature includes the name of the function.

reverse :: [a] -> [a] is easy to understand by it's name and type.

~~~
pvorb
I didn't critisize Haskell. I only criticized a statement about its type
system.

> You're also forgetting that signature includes the name of the function.

That's what I wanted to point out. The name of a function is even more
important than its types. Types alone seldomly give you the complete picture
of what's going on in a function.

Of course the name can be wrong whereas types can't, but the name is less
likely to be wrong than a comment.

------
ilovecaching
The languages that tend to become popular are simply languages that appeal to
the lowest common denominator. Java was the language invented for the "common
programmer", and people find that comforting. Now it's Go; as Rob Pike once
said of it, "They’re not capable of understanding a brilliant language but we
want to use them to build good software. So, the language that we give them
has to be easy for them to understand and easy to adopt."

That's why it's always a battle to push languages like Lisp, that offer
mathematical elegance over languages that are a soup of half baked idea that
veer programmers away from ideas that take more than a few days to master. For
those of us who aspire to do better, we should continue to try to bring the
from first principles approach of functional programming to the mainstream.

~~~
ausbah
Not gonna lie, to me that’s a lot of words to just say “people who use popular
languages are beneath me because they simply can’t understand the ‘beauty’
that mathematical-esque and functional functional languages offer”.

------
RandyRanderson
The issues mentioned are largely addressed by jshell and lambdas.

Regardless, his issues mostly boil down to "Java is not a REPL-first and
Lambda-first language". This is like me "hating" my cat because it's not a
dog.

I find many beginner programmers like REPL langs. When I talk to them, though,
it becomes apparent that they've never worked on a code base with 100k+ lines
with tens of colleagues.

One guy actually suggested that systems should never be that big! What do you
say to that? I just smiled.

~~~
s4vi0r
I think many Haskell, OCaml and Scala programmers would disagree heavily with
you.

Repl driven development works _very_ well with pure functional code and at no
point did I feel an increase in loc begin to make it tedious or whatever.

The repl approach perhaps doesn't work as well in Java because Java and its
ecosystem encourage writing messy jumbled code built upon unsafe abstractions.

------
RangerScience
I've recently come around on Java (although I'll still rarely choose it),
mostly due to the idea that "all things get complicated, so what matters is
how you manage that complexity".

Java started to "click" when I realized how much you build objects out of
other objects like layers of an onion, just adding a bit at each step, and it
clicked a bunch more as I understood Spring's bean system, which itself
clicked more when I began to understand that the object-based dependency graph
is basically the equivalent of the top-level Ruby script. All the code
describes how X and Y relate to eachother, and then you go to the bean
definitions to find out what X and Y are. That's actually pretty neat.

I still prefer the "simple script" approach you can do in Ruby; and I'm loving
the way Scala is kind of Ruby, but in Java, and so (and to relate to the
actual content) inherits a lot more from Lisp.

So, PS, author, if you're reading this, seriously give Scala a try. It's been
pretty fantastic so far for me!

~~~
reality_czech
Java has been gradually eating all the good parts of Scala. So far, it's eaten
lambdas, map / reduce / fold functions, option types, raw string literals, the
concept of a built-in REPL, type inference for local variables, and default
methods in interfaces (which is kind of like mixins). Pattern matching and
type classes are on the menu, but not yet formally merged into Java.

So what does Scala have left? Operator overloading, which tends to lead to
unclear code. "Implicit," which should give any former C++ programmer PTSD
flashbacks. A compiler which is pretty slow. The ability to embed XML into
code (why?). A collections library which is nightmarishly complex. (See
[https://yz.mit.edu/wp/true-scala-complexity/](https://yz.mit.edu/wp/true-
scala-complexity/) )

~~~
zmmmmm
> So what does Scala have left?

This is why I think long term Groovy is better positioned than Scala, even if
short term Scala has more of the mindshare. Groovy actually offers something
fundamentally different, while Scala is perpetually trying to compete on
Java's home turf. If people really want a pure, statically typed language
Kotlin is pretty good and has less impedance mismatch with Java.

~~~
tasuki
What fundamentally different does Groovy offer?

What do you mean by "Scala is trying to compete on Java's home turf"?

~~~
zmmmmm
Groovy is a truly dynamic language (with all the pros and cons that come with
that). So you can put it into contexts such as interactive scripting where
Java will always be suboptimal and solve problems Java doesn't even want to
solve. On the other hand, every single good idea that Scala comes up with will
eventually be co-opted by Java, because there is no reason for that not to
happen. It is just a matter of time.

~~~
tasuki
When higher kinded types come to Java, please ring me up!

~~~
zmmmmm
Touche - yes, I agree that Java is probably never going to attempt to
introduce that!

------
d_burfoot
It's ironic that the author claims that Common Lisp is easier to learn than
Java. One of the most common pro-Lisp, anti-Java arguments I hear is that Java
is too easy to learn, so that a lot of people who aren't actually very good at
programming can get Java jobs. This means that the Java world is populated
with not-so-bright people, while the rarefied Lisp world is full of elite
wizards.

~~~
ilovecaching
Java doesn't contain conceptually difficult features. In Rust you have to
learn lifetimes and borrow checking, in Haskell Monads... Java just has a huge
amount of relatively simple features. Lisp is middle of the road conceptually,
but is a small language.

So in that sense I agree with you. Java is favored by people who struggle to
learn harder concepts. That's why it's the usual target of the lowest common
denominator programmer and loved by large enterprises that can churn Java
programmers like mackerel.

------
apo
_Beside the REPL, another impressive feat of Common Lisp is meta-programming.
You can write software to write software. Say what? Yeah, that was my initial
reaction too, but Common Lisp’s macro system allows you to write functions
that return code snippets. It completely redefines the word. Or more
accurately, re-redefines, since Lisp is much older than MS Office, which is
what most people associate macros with, sadly._

The article could be improved by including an example. Pick one in which Java
makes the problem to be solved tedious, verbose, repetitive, or error-prone
and where Lisp macros do not. Bias the example as much as possible to a
problem where Lisp can shine. It should, however, be a real problem most
programmers are likely to face in some form.

Then present the source for both solutions.

This approach would even help those who don't use Java regularly because they
could try to implement the same functionality in their language of choice and
compare it the the Lisp implementation.

~~~
servbot
Here is something from Clojure that stuck with me as particularly elegant:

The -> macro.

    
    
      (-> {}
          (add-person 'servbot)
          add-age
          add-info
          add-to-db
          notify-status-by-email
          log-user-addition
      )
    

So the threading operator is exactly what you ask. It inserts the results of
the previous expression as the first argument as the first argument as the
next expression. Note this still supports situations where an expression can
take more than one argument.

Additionally, there is the ->> operator which inserts the previous result as
the last argument in the next expression and some a library that includes the
"magic wand" operator "-<>" ala

    
    
      (-<> {}
          (add-person 'servbot)
          (add-age 21 <>)
          (add-info 'servbot <> 21)
          (add-to-db 'my-db-handle 5432 <>)
          notify-status-by-email
          (log-user-addition 'dev 'email <> 'paint-by-numbers)
      )
    

This particular set of macros seems entirely trivial until you use it, and
then it changes your entire perspective of what functions do since your
ability to express the computation pipeline changes completely.

Edit: Update for formatting.

~~~
luminous_arbour
The Clojure standard library has had the the 'as->' macro for a while which
serves the same purpose as the "magic wand". It doesn't need to use '<>' as
the identifier, but I usually choose to do so because I learned about the
magic wand library before I learned about the 'as->' macro.

    
    
      (as-> {} <> 
          ...
          (add-age 21 <>)
          ...)

------
haglin
If you are running JDK 9 or later, you can use jshell:

jshell> "Hello World!"

$1 ==> "Hello World!"

[https://openjdk.java.net/jeps/222](https://openjdk.java.net/jeps/222)

~~~
chvid
And lambdas were introduced in 8 making nearly all of the author’s issues
mute.

~~~
hajile
What about closures? Also, those lambdas are actually classes that try very
hard to look like lambdas, but aren't actually.

~~~
akvadrako
What do you mean "aren't actually" ? Anything that behaves according to the
rules of a lambda is one — they can certainly be implemented with classes.

~~~
hajile
There are quite a few complications of Java lambdas and their abstraction
bleeds out in several edge cases. This is a result of their class
implementation and would not exist if functions had first class support in the
language and barcode bytecode (there are complications there too because they
didn't want to change anything at the bytecode level for lambdas).

[https://dzone.com/articles/java-8-lambas-limitations-
closure...](https://dzone.com/articles/java-8-lambas-limitations-closures)

~~~
colin_mccabe
Lambdas in Java aren't really that complicated. If you understand anonymous
classes, you understand lambdas.

That article just compares Java's closures with Javascript's closures. The
"limitation" is that Java's closures can only access the final variables of
the enclosing scope. But the article agrees that this limitation "can be
considered negligible" (seriously, read it-- that's the conclusion.)

Also, starting in Java 8, you don't have to explicitly declare things "final"
to use them in anonymous classes or lambdas. They just have to be effectively
final, meaning they are not mutated.

~~~
TeMPOraL
> _But the article agrees that this limitation "can be considered negligible"
> (seriously, read it-- that's the conclusion.)_

Working both in Java and Lisp professionally, it sure as hell isn't
negligible. It changes the way you write code. Lambdas in Java are 80%
solution. Java 8 really did make this language finally bearable, sometimes
pleasant to work with. 80% solutions are good, but the remaining 20% is not
"negligible".

------
vbsteven
A turing complete macro system is seen as a positive thing but if someone
dares to mention Java’s annotation based code generation and DI everyone is
complaining about “too much magic”

------
dang
Discussed at the time:
[https://news.ycombinator.com/item?id=3525927](https://news.ycombinator.com/item?id=3525927)

------
thosakwe
Nit-picking a specific claim made in the article, but I honestly don't believe
Lisp would be any less confusing to new programmers than Java. In fact, I
think it would probably just confuse them more.

------
wiz21c
FTA :

>>> But for a skeptic like myself — who spent 4 months intensively practicing
Tai-Chi in Beijing, just to confirm the existence of “qi”

Has anybody here experienced this chi thing. My tai chi teacher can surely
demonstrate it but I have hard times understanding with my occidental way of
looking at physics... My understanding it's more a state of mind that allows
the body to move in a very specific way.

------
commandlinefan
One big thing I notice about Java (and it’s ancestor, C) is that it’s designed
to make it easy to pinpoint exactly what went wrong, when something goes
wrong. Lisp, on the other hand, is designed so that you can get it right the
first time.

------
cutler
My biggest gripe with Java is it's insane backslash-enhanced regular
expressions. No other language seems to require this ridiculous butchering of
what brought me into programming.

~~~
colin_mccabe
Java 12 is going to add raw string literals which will fix this problem.
[https://openjdk.java.net/jeps/326](https://openjdk.java.net/jeps/326)

------
rurban
An old lisper would have used '(Hello World) over "Hello World". We are
dealing with symbols mostly, not strings.

------
kazinator
I tried reading the _Java Language Specification_ once, but it was such insane
garbage I stopped and never give it another look.

~~~
nayuki
I read parts of the JLS and I found the text quite down to earth compared to
the respective specifications of C, C++, and ECMAScript (JavaScript). Oh and
as for scripting languages like PHP, Python, and Ruby? They don't have a
standard because the implementation is the spec!

~~~
strzibny
Wrong. Ruby has (one) ISO standard:

[https://news.ycombinator.com/item?id=3868970](https://news.ycombinator.com/item?id=3868970)
[https://www.iso.org/standard/59579.html](https://www.iso.org/standard/59579.html)

------
shion
Lisp is the best language in the world. ^^

------
john_minsk
Why are we still discussing it?

JAVA is used in most companies in the world. Lisp on the other hand - is not
advised to be used in production.

To me it looks like long lost battle

