Hacker News new | ask | show | jobs
by collyw 3901 days ago
Java did the last time I tried it.
3 comments

I once worked on a project that was written in Groovy (a hipster version of Java, so to speak). At some point I have converted it from dynamic to static, by adding the @CompileStatic directive and adding some type declarations. It became MUCH more productive and maintainable. If I would start a new project on the Java platform right now, I would be certain to choose Groovy with @CompileStatic instead of Java. It has all the good things of Java without all the bad things.
I've found Groovy's only good for dynamically typed code...

* the stuff you want to write quickly but don't mind it running slowly, i.e. throwaway code

* the small stuff you know won't become a larger system one day, e.g. 30-line Gradle build scripts and code testing Java classes

* when you know you won't be upgrading to Java 8, which Groovy hasn't kept up with syntactically

Groovy's static compilation was tacked on for version 2.0 and doesn't work, except for sprinkling the occasional @CompileStatic around your code in a trial and error fashion. You didn't actually say you HAD started a new statically typed project in Groovy on the Java platform. If you do, use Java 8 which makes much of what Groovy brought to Java 7 redundant, or another language written from the ground up for static compilation, e.g. Scala or Kotlin.

In fact, I've found even for code testing Java classes, using Clojure is more productive than Groovy once you get over the syntax hurdle because macros can eliminate verbosity in repetitious test scripts in a way functions can't.

In the 1990s when Python et al were being created, static typing really sucked, plus you were almost always working in a really complicated environment like Windows or something where the details of the OS were sticking out of the types of way too many of the function you wrote.

So it was easy to switch to Python and come to the conclusion that it was all the static typing's fault, because that was the most obvious difference between the languages of the time and Python/Perl/etc.

But it's 20 years later now, and the statically-typed languages have not been standing still. They're a lot easier to work in now, even for prototyping in my experience. Go, which I mention since it's on topic, is reasonably fluid with its interfaces and structural typing. It isn't quite Python's level of fluid, but I also find it doesn't take that long before the static typing wins start to balance out anyhow when it tells me about problems before they've metastasized because I didn't know about them and kept going.

The whole functional programming world with its type inference is fairly fluid to work in, and when it gives errors, I find they tend to be real errors, even in your prototyping code, that really do need to be addressed before you can compile. (If you get good at it, prototyping is actually really easy in Haskell, but there is an up-front investment.) Optional/incremental typing, despite my general distaste for them [1], also can be easy to work with. And even some of the old school statically-typed languages are easier than they used to be, because you can find some abstraction layer that isolates you from the sort of thing that made the 1990s a really crazy time to program in.

So, it helps to use a language that doesn't date back to the time of Python's origination.

The only thing inconvenience that truly static typing brings to prototype code in my experience is that it really wants you to push a given change all the way through the code base, when you may just want to experiment with it on one code path. Generally there's a way to just beat the compiler down, though. And when you've learned the art of using types to mean things, and when you've learned to read what type errors mean and that they are not just a content-free shouting of "NO!", the ability to harness what the errors mean and react to them quickly in your design, rather than bake them into your code accidentally, can end up being a net gain, even in mere hours of work.

[1]: Mostly a consequence of the fact that, as I'm describing in the rest of this post, static typing is a lot easier than it used to be, which means I consider incremental typing to mostly be solving a problem that no longer exists. YMMV.

> But it's 20 years later now, and the statically-typed languages have not been standing still. They're a lot easier to work in now, even for prototyping in my experience. Go, which I mention since it's on topic, is reasonably fluid with its interfaces and structural typing.

What type system feature does Golang have that was invented post 1990?

Interfaces certainly were not created post 1990—Java had them, to name one obvious example—and structural typing is very old as well (and I don't think the sort of pseudo-structural typing that Go has for interfaces is particularly important for usability anyway).

"What type system feature does Golang have that was invented post 1990?"

Popularity and a set of libraries useful enough to do real work in it unlike anything else I know with structural typing.

What feature does Rust have that wasn't invented in some academic paper by the 1990s if not sooner?

An advance doesn't much matter to people doing real work if it doesn't come coupled to a practical language and a set of libraries that enables real work. This is sad in a way, because "the set of libraries that enables real work" bar is going up every year, which makes new languages harder. See also "Why did Haskell need 15 years to take off?", the answer being "that's when people started putting together practical libraries" among other things.

In 2025, when $PRACTICAL_DEPENDENTLY_TYPED_LANGUAGE is finally taking the Hacker News world by storm, I will not take kindly to suggestions that we could have and should have been using it in 2015 or earlier. No, we couldn't and can't. They're not useful yet, no matter how theoretically beautiful they could be.

If you concede that Go didn't do anything new other than getting popular, then I can't find anything to disagree with.

> What feature does Rust have that wasn't invented in some academic paper by the 1990s if not sooner?

Actually, the borrow check is novel (though you can find precursors in Cyclone and fractional permissions work), and I think that is responsible for a lot of Rust's popularity (as it enables memory safety with dynamic allocation and no garbage collection, something that is actually new). But the answer you were looking for is "nothing", and that's precisely my point in regards to Golang. This stuff isn't new. Specific repackagings of it are getting popular, but I think that factors unrelated to the technical details of the type system have been largely responsible for that.

(I also continue to disagree that structural typing as implemented in Go is a useful feature—I think it's a net negative for usability because it saves a few keystrokes at the cost of precluding implementing interfaces for types outside of the package they were defined in without writing annoying wrapper structs—but that's neither here nor there.)

"I think it's a net negative for usability because it saves a few keystrokes at the cost of precluding implementing interfaces for types outside of the package they were defined in without writing annoying wrapper structs"

It's a very pragmatic answer. As someone who generally straddles the pragmatic/theoretical divide, I mean that both fully as praise and fully as condemnation. ("Do I contradict myself? Very well, then I contradict myself, I am large, I contain multitudes." - Walt Whitman)

I never did hear what Rust decided to do about what Haskell called the "orphan instances" problem, I only heard about the problem... what happened there? (Honest question, not critical in any way.)

Orphan instances are forbidden in Rust. But you can still implement traits for types that weren't defined in your crate as long as your crate defined the trait, which is an extremely important feature that Rust code uses all the time (and is incompatible with structural typing).
Yep. Java looks good on paper, is mature, has good libraries, good IDEs and good support but boy is it painful to write it in any large quantity.
It's not that painful to write in large quantities, at least no more so than other static languages. And (given the IDE support), it's significantly easier to refactor code in Java than a dynamic language.
> It's not that painful to write in large quantities, at least no more so than other static languages. And (given the IDE support), it's significantly easier to refactor code in Java than a dynamic language.

Java does not have type inference, does it? That alone makes it more painful than other static languages.

Does Java have type inference?

In certain situations, like some generics declarations, and in the lambda syntax. Overall, no.

Even still, it's only a bit of typing, which has never really bothered me. Once typed, the available refactoring tools make it super simple to edit, and I find that I spend way more time editing code than creating it from whole cloth.