Why I love Common Lisp and hate Java (2012)
kuomarc.wordpress.com
kuomarc.wordpress.com
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.
Yes? It's great for debugging, which is generally what you'd use a print for anyways in a large application.
My comment explicitly stated dev environment, not prod, for debugger usage.
I would rather argue that such incompetent managers who speak such nonsense need to be fired.
"The Remote Agent software, running on a custom port of Harlequin Common Lisp, flew aboard Deep Space 1 (DS1), the first mission of NASA's New Millennium program. Remote Agent controlled DS1 for two days in May of 1999. During that time we were able to debug and fix a race condition that had not shown up during ground testing. (Debugging a program running on a $100M piece of hardware that is 100 million miles away is an interesting experience. Having a read-eval-print loop running on the spacecraft proved invaluable in finding and fixing the problem. The story of the Remote Agent bug is an interesting one in and of itself.)
"The Remote Agent was subsequently named "NASA Software of the Year"."
http://www.flownet.com/gat/jpl-lisp.html
http://ti.arc.nasa.gov/m/pub-archive/176h/0176 (Havelund).pdf
Of course we all hope for well thought out logging to troubleshoot issues we're seeing in prod.
I'm referencing an pattern I see with junior devs who simply use "printf debugging" in development instead of learning to use a debugger properly, even with distributed systems.
Whereas I see this pattern more with senior engineers.
Get on my level, normies. /s
I regularly see pais+ of println debuggers debate and speculate while the guy with the debugger drills straight down to the issue, and fixes it.
By using logging instead you’re reimplementing years of good work done by engineers before you.
1st you have to add the useless print statements and after decades of programming a debugger is vastly superios in terms of 'debugging' when compared to printing. (Back in time basic had no debugger even)
Also printing is utterly useless for high concurrent code, as printing alters memory visibility, usually adds global sync, etc..
The breakpoint would cause the method to be deoptimized, executed in the interpreter. Removing the breakpoint would allow the method to be optimized again.
Now obviously during stepping in, the thread would be blocked and not highly concurrent. However print statements just bare the concurrency.
I probably could have attached a remote debugger, and executed the relevant function a few times until my request got routed to the right process in the cluster, but that honestly would have taken me more time than just committing the print statement and letting CI take it away.
But you're right, figuring out a dev / prod discrepancy in already-running code is a case where a debugger is not as useful.
In many cases there is no debugger available for someone's favorite platform. And so they "hate debugging". Go programmers, javascript people (where you can't do client->server debugging, but really, really have to), ...
How many Java programmers don't use debuggers ? How many C# developers ? Those languages have excellent debuggers. Python, C/C++, ... decent at best. Go/Javascript/... dismal debugging support.
Eh? I thought JavaScript debuggers were pretty good.
On the other hand, I tend to use only print statements when I'm roughing in a program in an iPython Notebook. This gives a sort of visual documentation when navigating a library that I'm new to, and allows me to glance back at a verbose description of otherwise opaque data structures.
Yes, I do.
Now, I may have to work with a language/ecosystem that makes that awkward, sure, but that's a problem, if an incredibly common one (Julia is one of the few languages that avoids it.)
(Not that anyone would call Lisp approachable either, of course.)
You need to learn and memorise 15 facts to fully grok the Java Hello World snippet (including how to compile and run it), for Python it's only four facts.
That's a big difference in cognitive load.
A normal person who's sincerely trying to learn, when shown the Hello World will ask about everything - what is this "class" and why it's needed? What does "public", "static" and "void" mean, and why they're written. What "System.out" is? Not to mention, the package declaration at the top of the file.
All of those things have reasons for them, but when explaining this to a novice, you'll have to initially handwave most of those reasons away. That seems very uncalled for, especially when the basic abstraction you're trying to teach is that "computer executes simple instructions top to bottom, one at a time, and everything is built out of those simple instructions".
This design makes sense from conceptual purity, but Java is never a conceptually pure language. It distinguishes value types and object types, for instance. Not allowing free functions is really just a poor design choice.
Worse, the proliferation of single-method interfaces (which are really just functions) and static methods (which are in most cases really just free functions) really prove that free functions are useful. Java just makes it harder to use them.
I don't see why. The verbosity is pretty minimal and you enforce the availability of a privacy scope you wouldn't have had. ie private static methods and members that your public statics can use.
Although, pure functions are nice if you can't trust the code you're calling won't surprise you.
If you could show some examples of single method interfaces whose naming makes sense, I'd be delighted!
IDisposable with a single Dispose() method?
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.
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.
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.
I don't think that's true. Idiomatic Haskell has explicit declarations only for top level functions and uses type inference for everything else.
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.
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.
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.
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.
> 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.
In Haskell you start by defining your domain as types, and constrain those types with typeclasses. By the time you get to writing signatures, the implementations can almost be inferred (in Idris they actually can be inferred, the code literally writes itself).
In an imperative language with side effects, examples and documentation are a must because behavior is hidden in the body of a function; the signatures lie.
A journeyman Haskeller can write code that is completely self documenting. No examples are required, because the types coupled with the purity of the language allows us to tell the whole story.
I have experienced what you're talking about with Haskell but it was a lot of uncomfortable work. But it's "technically" true that the types often describe how to use a library, sometimes so well that errors cannot happen.
The same can be true of tests. I've started generating API documentation from mine: e.g. https://hitchdev.com/strictyaml/using/alpha/scalar/email-and...
I tend to think of types and tests as attacking the same problem from opposite directions.
Public Service Announcement: don't validate emails with regex. Every time you do, god kills a kitten.
Maybe that's partly because Haskell can easily become a bit too elegant. Between all the currying and combinators, the type helps to understand code "top down", i.e. when you don't have studied and memorized all "bottom up" component parts.
for idx, entry in enumerate(list)
vs. list iterators in Java where you have to repeat complex types at least 3 times and get about 5-10 times more code when it would only have to be about 2 times to achieve static type safety. Lack of typedef and the resulting drive towards dummy classes is exacerbating this problem further.
If that were the trade off, it would be a no brainer. But instead it feels like double the code, for no clarity (or negative clarity because the noise becomes obfuscation).
It's just convenient for a scripting language to be able to print stuff without any code structure. Something that Java enforces.
I've concluded it's because Java isn't designed for scripting that it doesn't allow scripting practices in its code bases. I just started learning Java and I guess if I use a class that could throw an exception I have to catch it (or transfer it to my class' user it seems). In this particular test I knew it wasn't gonna throw but it wouldn't compile: I was opening a file I knew existed for some quick test - instead of 1 python line I got 7 java ones. But with exception / catch structure.
It's an enterprise language, I'm all for the code structure enforcement. Help a dev catch an exception, so many things could already go wrong on larger codebases ;)
On the other hand,
"__name__ == __main__" is just a convenience for debugging and small scripts. I wouldn't use it in real programs.
I dont use @staticmethod, or find it useful. If I want a function, I use a function.
In principle, it makes sense to "bundle" functions that are strictly related to a class within the class itself (i.e. as static methods). Of course, the benefits viz. top-level functions are only noticeable if your codebase is big enough.
But again, they are "usability" benefits. I'm no CPython expert but I don't think that a static method would have noticeably worse (or better) performance than a function.
It is way more concise than java but does not become ambiguous because of it.
Prefer to use modules and tables of functions as an organising principle instead of classes.
Use inheritance rarely, and method overriding almost never. Otherwise you can make a really big mess (see Django's Form class hierarchy).
This sort of bad design where you have the flow of control jumping up and down the inheritance hierarchy happens in Java too, but the `abstract` keyword and static typing makes it easier to avoid.
In Java, make your code regular, so someone can understand the piece they're looking at without too much context.
In Python, make your code interesting. Factor out the unimportant repetitive parts, so someone can see a lot of the big picture all at once.
Incidentally, I'm quite happy with print() in the top-level namespace. Java's logging situation (5 widely used logging frameworks and a 6th to abstract over them all) is not better.
I personally liked it. It is a little overwhelming at first, but after you get used to it, it's actually quite powerful.
Cognitive load vs. verbosity can be visualized with something like Laffer curve - sure it's easier to read a couple lines of Lisp than one line of APL, but when you get into situations like one line of Lisp (or Python) gets expanded into 30 lines of Java, then the former is much easier. Terse languages make you pay cognitive cost for unpacking meaning, verbose languages make you pay for keeping track of what all these lines of code are doing. Ever had this situation when you read two pages of a book and realized you didn't really read anything, and have to re-read it (and 3 pages prior) to understand what's the point? That's Java in a nutshell, especially pre-1.8.
As for Lisp and cognitive load, the difficulty comes from a) dynamic typing, and b) macros. The former makes bad naming choices much more painful, as you can easily lose track of what's the code doing, and to what data. The latter are fine when written well, when written badly, your problem suddenly explodes in complexity as you have to peel off a layer of syntactic abstraction, and fix the underlying mechanism.
Not necessarily, but I definitely want functions to be accessible in the top-level namespace. Not everything is an object.
Python now has type hints and good static checking libraries so you can add as much type checking as your want. Java's type system is atrocious (c.f. Haskell, OCaml); it doesn't check nulls (a common bug), making simple constructs (tuples, unions) often requires a new class file of boilerplate.
The HelloWorldApp is a bad fit for objects. All the functions are static. This confused me for a long time learning Java. You can get a very long way in Python without knowing @staticmethod.
The static nature of Java does make it easy to know where to look for things and introspect code; there tends to be less magic.
And that's more "awkward" than the idea of a "public static void main" and a MANIFEST file because?
Strongly disagree. (Traditional) Java has no way to represent a function or a class as a first-class entity and it suffers for it. In Python functions and classes are plain old values (and indeed objects) and any value can live at top level in the module.
> * Python always executes the top level, so you end up with awkward idioms like: if __name__ == "__main__": main(sys.argv)
How is that any more "awkward" than the Java way? It's fewer lines of ceremony in all cases as far as I can see.
> * The "public" keyword might seem like noise, but in Python you denote private by prefixing the name with one or two underscores.
So Python has a) a better default (it's not that Java doesn't have a default visibility level, it's just such a spectacularly useless one that it never sees use) b) a concise symbolic syntax for something that's used extremely commonly, which is the right approach: http://www.lihaoyi.com/post/StrategicScalaStyleConcisenessNa...
> * 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.
Big applications will always have their own frameworks and no possible default set of imports makes sense for big applications (Java's default imports from java.lang. get in the way for big applications too). But a good language should work for small scripts as well as big applications, so having print in there by default makes sense. (I can sympathise with having a no-default-imports option or something like Haskell's custom preludes).
> * You still eventually need to learn what "@staticmethod" means in Python.
My 10+ years of Python say otherwise. You really don't.
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.
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.
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.
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!
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/ )
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.
I see this the other way around: Java genuinely has eaten most of the good parts of Kotlin, because Kotlin is positioned as a small enhancement over Java rather than a language that brings major improvements as Scala does. Adopting Kotlin means paying much the same cost as adopting Scala, but for much less in the way of benefits.
What do you mean by "Scala is trying to compete on Java's home turf"?
Scala doesn't stand still either. Dotty / Scala 3 will bring many refinements and new features like union types, opaque types and implicit function types.
If you adopted Scala 5 years ago, you could have had all the good stuff Java has today, 5 years ago. If you adopt Scala today you can have all the good stuff Java will be getting in the next 5 years, today.
> Pattern matching and type classes are on the menu, but not yet formally merged into Java.
How many years or decades will those "formalities" take? Java language enhancements have a history of taking much longer than originally claimed.
> Operator overloading, which tends to lead to unclear code.
The problem with traditional operator overloading is that you have to memorize which symbol corresponds to which magic method name (I can never remember what method * calls in Python, for example). Scala avoids that because it doesn't actually have operator overloading; rather, operators are just normal methods that you call in the normal way.
> The ability to embed XML into code (why?).
Already moved into an optional library, being dropped entirely in the next version.
> A collections library which is nightmarishly complex. (See https://yz.mit.edu/wp/true-scala-complexity/ )
Replaced in the next version. (And the complex parts were only ever for doing things that are completely impossible in any other language).
> So what does Scala have left?
Higher-kinded types (which let you represent secondary effects in a uniform way - obvious things like validation or async, but also custom effects like database transactions or an audit trail). Uniform representation of structures/records (via Shapeless) that lets you traverse data structures in a normal, type-safe way.
Put that together and you get a language where you never need the magical frameworks that (real-world) Java needs. No reflection-based runtime serialization that you have to control via a type registry separate from the language type system. No reflection-based database mapping that you have to control via another, subtly different type registry. No AOP decorators where you rename a method and it magically stops having a database transaction applied. No magical annotations for your http routes that tell it which string class name to load at runtime for serialization, where if you want your web threads to do something before/after you use yet another magic method or registry. No magical autowiring framework instantiating all your classes for you.
Just plain old functions and values. Standard language features (plus one macro in shapeless that might as well be part of the language for it's used) for all of the above. One standard syntax (for/yield) for effect management that all the language tools understand. Libraries for things that would be language features in most other languages, but without having to resort to the free-for-all of custom macros.
That's what Scala has. If Java catches up to that one day, great! But without higher-kinded types, and without some kind of uniform representation of records/case classes/data classes/what-have-you, it will never get close.
That's not even in the top 5 problems with operator overloading.
> How many years or decades will those "formalities" take? Java language > enhancements have a history of taking much longer than originally claimed.
Almost certainly a shorter amount of time than it would take to migrate our codebase away from Java. Oracle has actually been moving pretty fast with Java recently, for better or worse.
> [XML is...] Already moved into an optional library, being dropped entirely in > the next version.
And this illustrates another problem: Scala is constantly breaking backwards compatibility. Which is a real-world problem, unlike "I want the latest gee-whiz language feature."
> Put that together and you get a language where you never need the magical > frameworks that (real-world) Java needs.
Scala has plenty of "magical frameworks": scalaz, akka, shapeless, the list goes on.
Newer Java libraries like Jackson don't need a type registry (or at least, not one that have to manually set up.)
I won't try to defend J2EE or Hibernate, but also, I don't use them.
What other problem is there? Some libraries define functions with stupid names and that's a problem, but it's a problem you have without operator overloading too.
> Almost certainly a shorter amount of time than it would take to migrate our codebase away from Java.
I rather doubt that; a typical codebase has a half-life of what, 5 years? Which is less time than many of the Java features you listed have been delayed for.
> And this illustrates another problem: Scala is constantly breaking backwards compatibility.
Hardly. One major compatibility break in the language's entire history, eight years ago; removal of XML literals will be part of the second, and is only happening after they've been a) universally agreed to be a bad idea and b) deprecated for four years and counting. Yes it's not quite the extreme levels of backwards compatibility that Java offers, but it compares favourably with most languages out there.
> Scala has plenty of "magical frameworks": scalaz, akka, shapeless, the list goes on.
Maybe some parts of akka (I don't use it), but certainly not scalaz or shapeless: they're libraries, not frameworks, and there's no magic (no reflection, annotations or anything like that, aside from the one macro I mentioned), just ordinary values and ordinary functions that you call in the ordinary way (even the macro behaves like one).
> Newer Java libraries like Jackson don't need a type registry (or at least, not one that have to manually set up.)
Jackson is the example I was thinking of actually, it absolutely does have a (magic, reflection-based) registry that controls how things get serialised. See e.g. https://github.com/FasterXML/jackson-datatype-joda#registeri... .
Horray! I remember the first time I poked at Java, back in 2007, and I hated it, in large part because it was missing these. It's fantastic it's eaten these good things.
> So what does Scala have left?
Style/syntax and community?
I don't know enough about either language to debate on the points you've brought up; what I do know is that Scala feels a lot more like writing Ruby/JS/Python than writing C++/C#/Java.
I've only just started writing real class definitions in Scala, and the way that "constructors" work really drives home this similarity and difference; the kind of code you write to define the class looks to be the exact same kind of code you write to define everything else.
I also haven't poked into the larger Scala community, but I can't imagine it's all that similar to the larger Java community. Even if Java reaches some kind of feature parity with Scala, I bet this difference remains, and it'll come across in the libraries, common usage patterns, help on the internet, and conventions.
Again comparing to Ruby, the effect that "Matz is nice" and (when you add in Rails) "convention over configuration" have goes beyond code features into what it's actually like to use and work with the language day in and day out.
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.
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.
However, there's a reason Java is in the top 3 most popular languages. Its pragmatic, it scales, it plays nice with tooling and the language itself is pretty passable.
I'm currently dabbling in Android development, and the few Java libraries I've dealt with, especially Android's own APIs, have been a thorough exercise in frustration, to put it mildly.
Where are the good Java codebases hiding?
Have you looked at JDK itself?
I wonder if those are true reasons. I currently believe in the following set of reasons, in the order of importance: runaway feedback loop of popularity (popularity -> jobs -> popularity), JVM is cross-platform, low cognitive effort per line of code written.
That being said, I write Java for a living, and I don't find it terribly verbose, outside of the lack of sum types, pattern matching, and typeclasses.
So basically being able to run an arbitrary number of dependent, nested loops. Now imagine you want to run arbitrary code in the interior, rather than just incrementing a count.
It's a simple idea. And it can be expressed so succinctly in just a few lines of pseudocode in a freshman-level computer science textbook.
And yet I wouldn't want to touch this in Java. The lisp macro to write this isn't much more verbose than the pseudocode itself.
[0] Rosen's Discrete Mathematics and Its Applications, 7th e.
Edit: Traded formatting for pastebin
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.
(as-> {} <>
...
(add-age 21 <>)
...)jshell> "Hello World!"
$1 ==> "Hello World!"
I don’t think the argument in the linked article is very good but this apology is worse. It doesn’t really show any merit of Java (more that the repl in Java is much less powerful than in eg CL) or argue against the spirit of the article.
You're right, but to be fair the article is almost 7 years old, and Java has evolved a lot.
Take lambdas for example, the amount of arbitrary exceptions you have to keep in mind while using them makes it almost not worth the effort. It's still the same old crappy Java; only with shiny, barely working marketing gimmicks duct taped to the side.
https://dzone.com/articles/java-8-lambas-limitations-closure...
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.
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".
And Swift is a much better designed language for this than Java.
>>> 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.
https://news.ycombinator.com/item?id=3868970 https://www.iso.org/standard/59579.html
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