> Gradle is by far the worst-designed technology I've encountered in ~30 years of programming.
I nominate Groovy. Many of gradle's failures are just idiomatic groovy. The entire mindset of "friendly simplifications" that work 80% of the time, and if they give pretend that you don't notice, look, there's pretty code over there!
Even kotlin suffers from some of that (you can override a val x:Int with a val x:Int get() unless it's tied down otherwise, talk about principle of maximum surprise) but the genes from the other parent avoid the worst.
Can anybody explain to me why it ever seemed like a good idea to make both: (1) Semicolons at the ends of lines and (2) Function-call parentheses, not required, and not illegal, but optional? It strikes me as an abdication of the responsibility of a designer, like making both String::substring and String::substr available as aliases one to the other. Who cares? Choose one and stick with it! Otherwise it becomes a race between linter-writers and community programmers to see if the entire ecosystem will become polluted with an ugly mix of both styles. A race which the linter-writers always lose.
Groovy was created during the time when Ruby was taking off and was dramatically improving productivity of creating basic CRUD type web sites. So Ruby had a beautiful simple syntax, and I think Groovy was an experiment to see how close you could get to that while maintaining compatibility with Java syntax. It's actually a remarkable success as far as it goes - most Java code can be copy and pasted in and is valid Groovy. But of course it only works because it optionally accepts parentheses, explicit vs implicit arguments etc.
It does lead though to one of the biggest lies which is "if you know Java then you already know Groovy!" - that's just a mean trick to play on people.
Maybe the rest of Ruby is more "beautiful" and "simple", but those adjectives don't apply to the inconsistency caused by optional code punctuation. I think Ruby users even realized this, and have since imposed strict requirements on their projects that only one style be followed.
Groovy is all the more guilty for carrying the bad tradition forward. It's akin to somebody looking at the success of C and deciding that its macro system must be perfectly preserved going forward.
> I think Ruby users even realized this, and have since imposed strict requirements on their projects that only one style be followed.
A common set of conventions in the Ruby community uses both options for optional punctiation, with context controlling which is used for a particular case. E.g.,
1. Use a single expression per line, and don't combine the line starting or ending a block with the contents, and don't use semicolons where they are unnecessary (so, semicolons are used only to separate the opening and “end” of single-line blocks.)
2. Use parens among function calls, except omit them in calls that are part of internal DSLs.
And to make things more interesting at some point the nearly-frozen-in-time Java started evolving again so Groovy is stuck in some sort of parallel universe because Ruby failed to get mass traction and most people are not familiar with it while Java 8+ has its own concepts that don't translate as neatly as Java 6-7 concepts translated to Groovy at the time.
For Groovy the simple answer is they claimed that every piece of Java code would also work in Groovy. This wasn't ever true (well, maybe before someone decided that the equals() method is confusing), but it didn't matter, because of the "80% is plenty" mindset that groovy appears to be built on.
That would justify requiring semicolons/parentheses. It doesn't justify making them optional. The real explanation is that they were mimicking another language, Ruby, that had made that mistake.
Writing Groovy scripts in JMeter (for some custom requests) when doing distributed performance testing was a good way to enjoy pretty much anything else afterwards.
I suppose I should more accurately call that thing JSR223 + Groovy, all stored in an extensive XML file and edited through the JMeter UI..
Off topic, but if you plan on coding your load tests I think Gatling is a better choice, you get to program in Scala which so far looks clean and readable to me.
I think I conflate the two, and I'm not sure that's wrong. I've never seen anyone use Groovy outside Gradle. I mean why would you, if you had a choice?
I've had to use it to configure Jenkins.
In Groovy, variables are global if you forget to declare them with 'var'. This has caused so many concurrency bugs and so much sadness.
Especially before Java 8, writing Java code was very painful compared with Groovy, even though Groovy was fairly slower, and its compiler buggy. Groovy has closures, AST transformations builtin, much nicer syntax for properties (backed by getters/setters), ability to sort-of change classes at runtime (without instrumentation), multiple dispatch (i.e. dispatch based on the runtime type of the arguments), the ability to use reflection without the awful reflection API and so on.
Back in the days when Kotlin didn't exist and even Scala was in its infancy, some people genuinely liked the JVM ecosystem and even aspects of Java itself, but were turned off by the verbosity of Java. Those were the people that started using Groovy and also projects such as Grails (especially since they saw the success of Rails and Django and wanted to emulate that).
Also, I guess some people genuinely like gradual typing, although I've never found much use for it.
Nowadays, I wouldn't pick up Groovy anymore, but there was a time where it was basically that, or Java (or a different ecosystem entirely; or something like JRuby / Jython).
Groovy is an amazing language. Don't judge it based on Gradle. It is used for all kinds of things. Gradle sort of interacts with it in a highly toxic way and brings out all its bad parts. If you use it the opposite way - as a better version of Java with mostly structured code, plenty of explicit typing and avoiding the highly "magic" aspects that Gradle focuses on, then its a great language for application development. Certainly better for high level things than Java - but completely 100% compatible with the Java ecosystem.
Groovy is fantastic for quickly creating domain specific languages. I've done a bunch of them over the years for different problem domains and have yet to find another solution that yields effective custom languages that are as readable and easy to implement.
I have an old java (7, 8) app I maintain that whenever possible I move classes to Groovy as I am making other changes in them.
I would probably do Kotlin, now, but that wasn't an option when I started this.
The amount of boilerplate removed is one big win, but moreover the more functionally oriented aspects of most anything you'd do with a for-loop in java is wonderful.
And with annotations such as @CompileStatic and @TypeChecked, I am able to forgo SOME of the syntactic niceties for the compile-time binding java-comparable speed.
There are worse things than using Grails. For example using straight up Spring. Grails is a sane wrapper around Spring so you can easily bring regular Java developers into a sane ecosystem. For example Grails' Gorm is the only sane way of using Hibernate in the entire JVM ecosystem. Using Hibernate directly will drain your sanity very quickly.
Kotlin is playing it fast and loose with blurring the line between fields and bean-style properties. In Java you could have a public final field and it was obvious that it was immutable (unless referencing a mutable object). Kotlin, in an attempt to match Groovy in syntax cuteness, allows to declare a val (supposedly the equivalent of a java final field) that is actually not backed by memory at all but a getter method in disguise.
val x: Int // this is an immutable value
val y: Int // this is an immutable Function0<Int> that is executed each time and but pretends to be a val
get() = System.currentTimeMillis()
I think the reason for that is that Kotlin doesn't necessarily aim for immutability as much as it aims for read-only values. This is also why there is no truly immutable implementation of `List` in the stdlib, although the interface itself it read-only (but it can still point to a value that is being mutated somewhere else).
I agree that true immutability - or code that is more obvious about avoiding side-effects - could be useful, but I don't think that was ever the goal.
That's certainly an accurate description of the cause. But I don't think that it's a valid excuse: not supporting immutability well is fine, pretending that you do when actually you don't is not.
The kotlin defense to this surely is that immutability happily exists in val constructor args (with the usual exception: not transitive through references), but that's purely contextual and a difference that is very hard to represent at usage site (e.g. they look exactly the same in the intellij "javadoc" view). If for some reason bean-style methods had to be represented through var/val syntax, I think that it would have been much better if read-only calculated getters were represented by a var without set(), leaving val to cases where you're guaranteed to get the same when referenced twice (value set once in constructor or some lazyness shenanigans).
Supporting properties is one thing, having assignments copy the backing property as a lazy evaluated function-pointer another, even if value is supposedly a int.
Python getter properties are evaluated instantly on assignment. To convert them back into function-pointers like the Kotlin example above you really have to go out of your way by deep diving into inspect module. The result is a property-object which is not assignable to a int-variable, but type checking probably wouldn't go this deep as inspect naturally is very dynamic in its nature.
My guess is Kotlin does this as a way of structural typing, if it walks like a int and talks like an int it is an int.
Do those others heavily advertise the difference between var and val as a core feature and then sabotage it by dressing up getters (that are evaluated on each call) as immutable values?
I think the main thing to understanding this is that Kotlin tries really really hard to not have fields. They're all properties, similar to get and set calls in Java (and if you have a feild in an interface it's basically that).
I nominate Groovy. Many of gradle's failures are just idiomatic groovy. The entire mindset of "friendly simplifications" that work 80% of the time, and if they give pretend that you don't notice, look, there's pretty code over there!
Even kotlin suffers from some of that (you can override a val x:Int with a val x:Int get() unless it's tied down otherwise, talk about principle of maximum surprise) but the genes from the other parent avoid the worst.