Most active commenters
  • bsaul(10)
  • breadwinner(10)
  • int_19h(7)
  • neonsunset(4)
  • asa400(3)
  • (3)
  • kllrnohj(3)
  • vips7L(3)
  • treyd(3)

←back to thread

253 points chhum | 117 comments | | HN request time: 2.402s | source | bottom
1. nelup20 ◴[] No.44009800[source]
I personally appreciate Java (and the JVM) much more after having tried other languages/ecosystems that people kept saying were so much better than Java. Instead, I just felt like it was a "the grass is greener" every time. The only other language that I felt was an actual massive improvement is Rust (which so far has been a joy to work with).

It's a shame imo that it's not seen as a "cool" option for startups, because at this point, the productivity gap compared to other languages is small, if nonexistent.

replies(6): >>44009912 #>>44009928 #>>44009952 #>>44010109 #>>44010282 #>>44010468 #
2. sapiogram ◴[] No.44009912[source]
My feelings exactly. Go was particularly disappointing, it promised everything but only felt like a sidegrade from Java. Screw it, a downgrade, until go errors get stack traces.
replies(3): >>44010013 #>>44010083 #>>44010285 #
3. bdangubic ◴[] No.44009928[source]
same! mid-way through my almost 3 decade career I got bored and thought lets try some non-JVM projects, for 2 years I took projects using other languages etc… worse two years of my career :)
4. bsaul ◴[] No.44009952[source]
Funny. I've been trying rust for the past 2 months fulltime, and i'm really wondering how you can call it a "joy to work with" when compared to java, at least for server development.

Rust feels like walking on a minefield, praying to never meet any lifetime problem that's going to ruin your afternoon productivity ( recently lost an afternoon on something that could very well be a known compiler bug, but on a method with such a horrible signature that i never can be sure. in the end i recoded the thing with macros instead).

The feeling of typesafety is satisfying , i agree. But calling the overall experience a "joy" ?

replies(5): >>44010045 #>>44010053 #>>44010107 #>>44010130 #>>44012365 #
5. overfeed ◴[] No.44010013[source]
The reason I prefer the Go ecosystem to Java is cultural, rather than technical. Sure, the JVM is very impressive and the language has been evolving, but the culture around Java seems to encourage needless complexity.

Of all the languages I've had to work with trying to get to know unfamiliar code-bases, it's the Go codebases I've been quickest to grok, and yielded the fewest surprises since as the code I'm looking for is almost always where I expect it to be.

replies(3): >>44010172 #>>44010374 #>>44010391 #
6. mynameisash ◴[] No.44010045[source]
> I've been trying rust for the past 2 months fulltime,

> recently lost an afternoon on something that could very well be a known compiler bug

With respect, at two months, you're still in the throes of the learning curve, and it seems highly unlikely you've found a compiler bug. Most folks (myself included) struggled for a few months before we hit the 'joyful' part of Rust.

replies(4): >>44010298 #>>44010342 #>>44010401 #>>44011452 #
7. asa400 ◴[] No.44010053[source]
I'm not discounting your experience, or saying you're wrong or anything like that. I've been writing Rust for a while, and for me, that feeling went away. Lifetime/ownership problems used to run me over but it almost never happens anymore. I think Rust has a rather severe "hump" that a lot of people run headlong into, but it does get better, I promise you. You start thinking in ownership/borrowing terms and it just fades into the background at some point. Rust is easily my most productive language now. Again, not discounting your experience at all, it can be rough starting out.
replies(2): >>44010395 #>>44010691 #
8. ◴[] No.44010083[source]
9. ◴[] No.44010107[source]
10. deepsun ◴[] No.44010109[source]
My opinion on that is the projects scale. Typically people came from a 10+ year Java project of 100+ engineers, to a greenfield advanced hello-world -- of course it's going to feel better and more productive.

Also, as open-source folks say, "rewrite is always better". It also serves as a good security review. But companies typically don't have resources to do complete rewrites every so often, I saw it only in Google.

replies(1): >>44010246 #
11. worik ◴[] No.44010130[source]
> I've been trying rust for the past 2 months fulltime

Rust has a horrid learning curve

I've programmed for decades in many languages, and I felt the same as you

Persevere.

Surrender! to compile

Weather the ferocious storm

You will find, true bliss

replies(1): >>44010497 #
12. skydhash ◴[] No.44010172{3}[source]
I think kotlin is what Java should have been like. The same capabilities but with less cumbersome constraints.
replies(2): >>44010333 #>>44010696 #
13. sabellito ◴[] No.44010246[source]
I've worked in 3 of the biggest rails codebases in the world (Shopify being the last) and I can say from experience that rails legacy monoliths are infinitely worse to work with than some awful, but harmless, sea of struts XML legacy.
replies(1): >>44012394 #
14. kllrnohj ◴[] No.44010282[source]
Personally I think C# is miles ahead of Java and in meaningful ways (like a drastically better implementation of generics, not to mention value types have existed for eons at this point and an FFI system that doesn't hate you for using it)

But nobody seems to talk about or care about C# except for Unity. Microsoft really missed the boat on getting mindshare for it back in the day.

replies(12): >>44010302 #>>44010329 #>>44010450 #>>44010481 #>>44010493 #>>44010636 #>>44010692 #>>44010825 #>>44010902 #>>44011079 #>>44011124 #>>44012556 #
15. arccy ◴[] No.44010285[source]
funny. java is known for its stacktraces where you need 3 vertical monitors stacked together to see the whole thing and it still doesn't tell you anything useful about why the app crashed.
replies(5): >>44010426 #>>44010656 #>>44010801 #>>44011174 #>>44020250 #
16. johnisgood ◴[] No.44010298{3}[source]
Okay, but I have hit the joyful part of some other languages long before a few months. What gives?
replies(1): >>44015981 #
17. echelon ◴[] No.44010302[source]
That's because the Microsoft of 2000 - 2014, the Ballmer era, was Microsoft-first. It didn't care about other platforms, it didn't care about the web, and it didn't care about open source. C# could be great, but it existed in a bubble.

Java kept growing and wound up everywhere. It played nice with Linux. Enterprise Mac developers didn't have trouble writing it with IntelliJ. It spread faster because it was open.

Satya Nadella fixed a lot of Microsoft's ills, but it was too late for C# to rise to prominence. It's now the Github / TypeScript / AI era, and Satya is killing it.

The one good thing to say about Ballmer is that he kicked off Azure. Microsoft grew from strength to strength after that.

replies(2): >>44010348 #>>44011550 #
18. jayd16 ◴[] No.44010329[source]
Its actually quietly very popular in very boring contexts outside of Silicon Valley. Its also slowly entering new areas now that Linux builds are rock solid.

MS does have an uphill PR battle though.

19. ori_b ◴[] No.44010333{4}[source]
When I used Kotlin, it felt like Java, but with the antipatterns baked in as language features.
replies(1): >>44010677 #
20. bsaul ◴[] No.44010342{3}[source]
i didn't "find" it. As i said, it's a well known one : https://github.com/rust-lang/rust/issues/110338

Simply using axum with code using multiple layers of async was enough.

But then again, it looked like this bug (the error message is the same), however at this point i'm really unsure if it's exactly the same. The error message and the method signature was so atrocious that i just gave up and found a simpler design using macros that dodged the bullet.

21. ◴[] No.44010348{3}[source]
22. whartung ◴[] No.44010374{3}[source]
The don't embrace that culture. Embrace a simpler culture. Strive for simplicity. Push for fewer dependencies.

Simple example, JAX-RS running on top of Java SE. I agree, JAX-RS is not what one might call "simple". It IS complex, or I should say, it CAN be complex. But Happy Path, staying in the middle of the road, it's pretty sweet for knocking out HTTP backed services. The Jersey reference implementation will do most anything you need (including stuff not "included" in raw JAX-RS). No need for a container, no need for a lot that stuff and all that hanger-on. The core runtime is pretty broad and powerful.

Consider my current project, it uses the built in Java HTTP server. Which works! It's fast, it's functional, it's free. (Yes, it's in a com.sun.net... package, but it's not going anywhere.) It's awkward to use. It's aggravatingly raw. It follows the tenet "why be difficult, when, with just a little effort, you can be impossible."

So, I wrote a simple "servlet-esque-ish" inspired layer for response and request handling, a better path based regex-y router, and a nicer query parser for queries and forms. 500 lines. Add on a JSON library and I can process JSON-y web request/response, easily. (I also wrote my own Multipart processor -- that was another 500 lines, boy that was fun, but most folks don't need that.)

A little bit of code and the built in server is MUCH easier to use. No tomcat, no deploys, zip. ...and no dependencies (save the JSON library).

Something all of these cool frameworks and such have shown me is what's really nice to have, but at the same time, just what isn't really necessary to get work done. I mean, CDI is really neat. Very cool. But, whoo boy. So I have a single singleton to handle application life cycle and global services. It works great with tests. I have a 50 line Event Bus. I have a 100 line "Workflow Engine". 150 line java.util.Logger wrapper (which is mostly, you know, wrapper). I wrote that way back whenever they introduced varargs to java (Java 5? 6?). The modern Java logging landscape is just...oh boy. I'm content with JUL -- I can make it work.

My current project is "magic free". I think @Overide in the single annotation anywhere in it. But it's comfortable to use, the cognitive load is quite load (outside of the actual application itself, which is NOT low -- sheesh). No swearing at frameworks. It's all my fault :).

Anyway, the point is that "simple Java" lurks in there. It needs a bit of uplifting, but not a lot.

replies(2): >>44010903 #>>44011419 #
23. LtWorf ◴[] No.44010391{3}[source]
True, but the culture around go isn't any better. In my experience go developers are former java developers so they have the same culture of thinking it's ok to ignore how a unix system works. So you will have awful logging, daemons that never report they're ready, badly handmade command line parsing and so on.
24. bsaul ◴[] No.44010395{3}[source]
i do feel much more comfortable with lifetimes than at the beginning. I get the general concept, and i barely have to explicitely set them (i did start by religiously reading the rust book, of course).

However, at the moment i still feel i'm using a huge amount of layers upon layer of complex type definitions in order to get anything done. Just using an object's reference across async calls in a safe manner leads to insane types and type constraints, which read like ancient egyptian scripture. And at every layer, i feel like changing anything could blow everything up with lifetimes.

The language has this very special feel of something both advanced and extremely raw and low-level at the same time. It's very unique.

replies(1): >>44010456 #
25. heavyset_go ◴[] No.44010401{3}[source]
IME, new users do weird things that can expose bugs no one ran into because they wouldn't think to do things the weird way.
replies(1): >>44010594 #
26. marginalia_nu ◴[] No.44010426{3}[source]
I think that's a bit unfair. Generally the stacktraces tell you exactly what and where the problem is. Generally shouldn't be more than a dozen lines or so.

The main area they get excessively lengthy is in certain frameworks and testing tools that can add like 100 lines to the trace.

27. bsaul ◴[] No.44010450[source]
Problem with C# isn't the language, it's the enterprise ecosystem. You always feel like you're going to have to pay at some point down the road for using the tech.
replies(2): >>44010574 #>>44010608 #
28. charlotte-fyi ◴[] No.44010456{4}[source]
In my experience, beginners often make the mistake of assuming just because you can do things with references and lifetimes that you should. Unless you’ve done profiling, just start with the easy thing and clone an `Arc` (or whatever other data structure helps you avoid lifetime problems)!

Also, it’s worth saying, you probably don’t need async.

replies(1): >>44010556 #
29. watwut ◴[] No.44010468[source]
Same here. I started to really like java after working with js/ts on small to medium project. I started to love java after working in on medium to little big project.
30. watwut ◴[] No.44010481[source]
Definitely not. All the stuff I was used to get for free and advice I that was one google search away ... just dont exist in dotnet. Or is expensive. But most of the time, there is weaker library not doing nearly enough and that is all that exists.
31. dagw ◴[] No.44010493[source]
nobody seems to talk about or care about C# except for Unity

C# is extremely popular in all kinds of 'boring' industries. Having worked in areas like logistics and civil engineering, C# is everywhere.

32. bsaul ◴[] No.44010497{3}[source]
I am keeping up, but honestly i feel like rust dev is a bit like a trap : people like us love challenges and solving technical problems. So whenever we "triumph" over a rust tricky compilation issue, we feel intense gratification in solving it.

However, at some point you have to ask yourself why you're accepting to face all those challenges. Is it worth it ? When was the last time i faced a race condition when developping a backend ?

The reason i started with rust was for a very specific need on building a cross-platform library (including wasm), and that was a good justification, and i'm happy that i did. However now that i'm using it for the server as well and face the same kind of challenges, i seriously question whether this is a wise choice.

replies(1): >>44011576 #
33. bsaul ◴[] No.44010556{5}[source]
oh, believe me, cloning and Arc-ing is something i realized very early that i was going to use. But that's the point: you still end up with things like : Arc<tokio::RwLock<dyn MyTrait>> for every single dependency.

Then you want to declare an async function that takes an async closure over that dependency. And you end up with a total garbage of a method signature.

As for async, the ecosystem for server-side is totally filled with async everywhere now. I don't think it's realistic to hope escaping those issues anyway in any real-world project. i thought i might as well learn to get comfortable with async.

34. cgh ◴[] No.44010574{3}[source]
Every large company I’ve ever worked at had a strict “no Microsoft on the server” policy and for better or worse, C# is closely identified with Microsoft.
replies(1): >>44011041 #
35. bsaul ◴[] No.44010594{4}[source]
I hope the same, and TBH it's the only reason i'm keeping up developing that backend in rust. I hope that in the end, it's going to improve my style.

Go felt the same way (but with a much lower order of magnitude) : you feel like bumping into language limitations, but once you learn to do it "simply" in go, your style will have changed into something much more elegant.

As for the bug in question, it has been quite "popular" for about 5 years now, and is actively tracked : https://github.com/rust-lang/rust/issues/110338. Nothing really weird. Just async hitting the limits of the current rust design.

36. hu3 ◴[] No.44010608{3}[source]
I don't see Java being any better in this regard.

Microsoft has been historically much less aggressive with lawyers compared to Oracle.

replies(4): >>44010740 #>>44010762 #>>44011086 #>>44011178 #
37. nextos ◴[] No.44010636[source]
C# had the chance to learn from Java. Java was a bit rushed. Gosling said he was not given enough time to add closures. Even without generics, Java would have been quite elegant and much less verbose. The alternative, anonymous inner classes, were quite clunky.

Nevertheless, as a platform, the JVM and JDK were fantastic and miles ahead most alternatives during the late 1990s and 2000s. The only platform for large development that offered some compelling advantages was Erlang, with BEAM and OTP.

replies(1): >>44010753 #
38. hu3 ◴[] No.44010656{3}[source]
as someone who had to deal with Java stack traces the entire week, I feel attacked.

God bless Gemini 2.5 Pro which ate all the traces for breakfast.

39. complexworld ◴[] No.44010677{5}[source]
Could you explain which antipatterns you're referring to?
replies(1): >>44011457 #
40. debugnik ◴[] No.44010691{3}[source]
Not all lifetime problems are just a matter of learning. I've been picking up Rust every few years to catch up, and every single time I've eventually expressed a lifetime that either required unstable Rust at the time or hit a known lifetime bug.
41. breadwinner ◴[] No.44010692[source]
One area where C# really messed up is exception handling.

See https://mckoder.medium.com/the-achilles-heel-of-c-why-its-ex...

replies(4): >>44010987 #>>44011072 #>>44011368 #>>44011745 #
42. Zambyte ◴[] No.44010696{4}[source]
It is all but impossible to use Kotlin without an IDE telling you everything, and for that I find the language interesting. And for comparison, I did in fact write Java without an IDE for an extended period of time.
43. frontfor ◴[] No.44010740{4}[source]
That might be true for other Oracle products like the DB, but has that been true for Java and openjdk?
replies(1): >>44013068 #
44. hocuspocus ◴[] No.44010753{3}[source]
Java could have easily caught up with C# in the early 2000s. In 2001 Pizza (Scala's ancestor) showcased generics, higher-order functions, ADTs and pattern matching. Interestingly enough only generics made it to Java in a reasonable timeframe, the rest came only much later.

Aside from early versions being rushed, I feel that Java's success and adoption were the bigger issue. While Microsoft could iterate quickly and break backwards compatibility with major versions of C# and the .NET runtime, Java was deliberately moving at a much slower pace.

replies(1): >>44020224 #
45. wslh ◴[] No.44010762{4}[source]
I think OP means that .NET has strong connections with Microsoft technologies where it is a natural decision to use Azure instead of AWS.
46. frontfor ◴[] No.44010801{3}[source]
Compared to Go where I always have to remember to print the stack trace in every goroutine’s panic handler or use a custom error type that includes the stack trace or I get nothing? And I have to do this very basic thing for every service I spin up? This might not matter for pet projects or CLI applications, but it matters a lot in large scale mission critical servers.
replies(1): >>44012899 #
47. wokkel ◴[] No.44010825[source]
It barely works on non MS platforms,has had many slightly incompatible versions so it's a non starter for many projects where I run into that might benefit from c#. I spoke to a manager recently who had invested in silverlight in the past. Based on that alone it was a no MS policy for his development teams.
replies(4): >>44011050 #>>44011156 #>>44015516 #>>44122725 #
48. astura ◴[] No.44010902[source]
C# isn't popular in startups for the same reason Java isn't, it's "boring."

It's an extremely common language in "boring" companies doing "boring" (but profitable) work.

49. wpollock ◴[] No.44010903{4}[source]
You've made your point, but note the built-in HTTPS server only supports TLS 1.2, so don't use that for production code. (For testing it's probably fine.)
replies(1): >>44011244 #
50. easton ◴[] No.44010987{3}[source]
They intentionally chose to not have checked exceptions though, and people have different opinions on it. They believed that people would just catch Exception most of the time anyway instead of selecting on a very specific type (which is often how it works, in web apps anyway.)

https://www.artima.com/articles/the-trouble-with-checked-exc...

replies(1): >>44011111 #
51. runjake ◴[] No.44011041{4}[source]
IOW Linux on the server?
52. runjake ◴[] No.44011050{3}[source]
AFIACT, either most or at least a significant percentage of dotnet code runs on Linux, depending on the industry.
53. neonsunset ◴[] No.44011072{3}[source]
IIRC all previous conversations about checked exceptions here ended up with the swift conclusion that they are heavily discouraged throughout Java code.
replies(1): >>44011113 #
54. astrange ◴[] No.44011079[source]
> But nobody seems to talk about or care about C# except for Unity. Microsoft really missed the boat on getting mindshare for it back in the day.

There was this guy Miguel de Icaza. From when I followed the open source ecosystem at the time, it seemed to be his personal mission to promote independent clones of a bunch of Microsoft technologies like C# on his own time even though they didn't ask him to do it.

I don't think I ever understood why someone would do this. It's like in the 2000s where people seemed to think you could solve all technical problems by inventing new kinds of XML.

replies(1): >>44013990 #
55. antod ◴[] No.44011086{4}[source]
The poster was talking about the ecosystem not the core platform. The wider Java ecosystem was heavily dominated by many Apache (and other) open source libraries vs commercial products for the C# ecosystem.
replies(1): >>44011133 #
56. breadwinner ◴[] No.44011111{4}[source]
> They believed that people would just catch Exception most of the time anyway

Their belief was wrong. Microsoft now recommends against catching Exception.

The article you linked to is addressed at the bottom of this article: https://mckoder.medium.com/the-achilles-heel-of-c-why-its-ex...

57. breadwinner ◴[] No.44011113{4}[source]
> they are heavily discouraged throughout Java code

That's so ignorant. Read the article please.

replies(1): >>44011181 #
58. neonsunset ◴[] No.44011133{5}[source]
Most of the features that require Apache or community packages in Java ship out of box in .NET (or via extension packages).

They also tend to be of higher quality and provide better performance.

59. neonsunset ◴[] No.44011156{3}[source]
Quite a few languages "loved" by "Linux communities" tend to have rather rudimentary integration with Linux kernel's facilities or worse packaging story.

For example, Go does not understand cgroups limits and needs an external package to solve this. .NET can read and accommodate those natively. It also ships with excellent epoll-based socket engine implementation. It's on par with Go (although I'm not sure which one is better, but .NET performs really well on high-throughput workloads).

60. jjav ◴[] No.44011174{3}[source]
> stacktraces where you need 3 vertical monitors stacked together

If you wrote code with such deep stacktraces, it's all on you.

There's a performance cost to all that excessive stack depth too, often.

61. cyberax ◴[] No.44011178{4}[source]
Java has a strong history of OpenSource, and a great set of libraries. It also pioneered the managed dependency system early (Maven), so these libraries have been centrally available for two decades.

Moreover, a lot of these libraries are well-supported to this day. For example, Hibernate (the best ORM in business) is 28 years old, and has just released a new version. I recently consulted my former client (from 15 years ago), and I still recognized most parts of the stack that I set up way back then.

62. neonsunset ◴[] No.44011181{5}[source]
Let's revisit past conversations:

- https://news.ycombinator.com/item?id=43226624

- https://news.ycombinator.com/item?id=43584056

- https://news.ycombinator.com/item?id=36736326

And more. I'm not sure what you found in (checked) exceptions. If you'd like explicit error handling, we have holy grail in the form of Rust which beautifully solves it with implicit returns, error type conversions and disambiguation between error and panic model. I'd prefer to use that one as it actually reduces boilerplate and improves correctness, the opposite to the outcome of using checked exceptions.

replies(2): >>44011309 #>>44011760 #
63. whartung ◴[] No.44011244{5}[source]
TLS is supplied by the underlying JDK, which has been 1.3 for some time. How is HttpsServer not 1.3?
replies(1): >>44016315 #
64. breadwinner ◴[] No.44011309{6}[source]
> I'm not sure what you found in (checked) exceptions.

I could copy/paste the entire article here... but it would be easier if you could take a gander: https://mckoder.medium.com/the-achilles-heel-of-c-why-its-ex...

Summary:

Crashy code: You have no compiler-enforced way to know what exceptions might be thrown from a method or library.

More crashy code: If a method starts throwing a new exception, you might not realize you need to update your error handling.

Dead code: If a method stops throwing an exception, old catch blocks may linger, becoming dead code.

replies(2): >>44012858 #>>44013364 #
65. jayd16 ◴[] No.44011368{3}[source]
Java streams and a lot of other APIs are extremely ugly because of checked exceptions. Conversely, LINQ and delegates and a lot of other syntax is far cleaner in C#.

Your linked blog is pretty wild. Only throw RuntimeExceptions to crash? Why not just Exit if that's the proper thing to do?

If you treat all C# exceptions as RuntimeExceptions, then it satisfies the blog anyhow.

replies(3): >>44011399 #>>44011441 #>>44015573 #
66. breadwinner ◴[] No.44011399{4}[source]
> Why not just Exit if that's the proper thing to do?

Because you won't get a stack trace.

67. password4321 ◴[] No.44011419{4}[source]
You've made the project yours, nice! How many others work on it?

It's an uphill battle to convince my co-workers to do things my way.

68. vips7L ◴[] No.44011441{4}[source]
Checked exceptions could still work across lambdas. Scala is doing research there: https://docs.scala-lang.org/scala3/reference/experimental/ca...
replies(1): >>44020234 #
69. vips7L ◴[] No.44011452{3}[source]
> With respect, at two months, you're still in the throes of the learning curve,

It shouldn’t take 2 months of full time programming to beat the learning curve.

70. ori_b ◴[] No.44011457{6}[source]
Leaning heavily into object orientation, including baking in things like companion objects, object expressions, etc. Encouraging utility classes that tack half-baked functionality onto existing types. Smart casts encouraging overly complex hierarchy.

While operator overloading and infix functions aren't a Java anti-pattern, I also think the language would be improved by their removal.

replies(1): >>44020244 #
71. fuzztester ◴[] No.44011550{3}[source]
>Satya Nadella fixed a lot of Microsoft's ills,

which ones?

replies(1): >>44011676 #
72. worik ◴[] No.44011576{4}[source]
Generally Rust is the wrong choice.

Generally garbage collectors are very worthwhile

But whe you need it, it is so much better than C or C++

73. echelon ◴[] No.44011676{4}[source]
So many.

Microsoft isn't thought of as evil anymore, but is open source and Linux friendly. GitHub, VScode, Typescript. Azure is booming, ...

But the big one: stock price.

replies(2): >>44014257 #>>44020211 #
74. bigstrat2003 ◴[] No.44011745{3}[source]
I don't know that I'd say "messed up", but I do wish C# had checked exceptions. I strongly believe that they are superior to non-checked exceptions, basically static type signatures but for error handling. It's a real pity that everything after Java seems to have abandoned the idea.
replies(1): >>44014344 #
75. bigstrat2003 ◴[] No.44011760{6}[source]
> I'd prefer to use that one as it actually reduces boilerplate and improves correctness, the opposite to the outcome of using checked exceptions.

Reducing boilerplate is not a valuable goal in and of itself. The question is, does the boilerplate buy you something? I think that with checked exceptions it does. Having an explicit type signature for what errors a function can raise improves correctness a great deal because the compiler can enforce the contracts of those functions.

I agree that the Rust approach is good too, though I don't agree it has any strong advantages over the way Java does things. Both approaches are equally respectable in my view.

76. treyd ◴[] No.44012365[source]
You eventually learn you leverage lifetimes. You just need to get over that point in the learning curve. I very rarely run into those types of issues, and it's very freeing knowing that I can write libraries that are so hard to misuse because you can leverage the type system.

And macros are a part of that!

replies(2): >>44012714 #>>44012800 #
77. stevoski ◴[] No.44012394{3}[source]
I find this really interesting, and meets my (limited) Rails experience and (extensive) Java experience:

I found it hard taking over an existing Rails project - it felt frail to me, that any small change might have unexpected consequences.

Whereas when I've taken over Java projects - or come in late to an existing team - I felt quite confident getting started, even if it is a bit of a mess.

replies(1): >>44012707 #
78. throw1235435 ◴[] No.44012556[source]
Depends where you work. I've seen a lot of Java and .NET jobs around where I am. Having both jobs I find it easier to get performant code in .NET than Java (reified generics, value types, more primitives/less boxing in general, and other constructs not found in Java)
79. sabellito ◴[] No.44012707{4}[source]
It's mostly because of the compiler, but ruby also makes it extremely easy to shoot oneself in the foot with magic.
80. rwmj ◴[] No.44012714{3}[source]
Why do I need to? Why can't I let the garbage collector deal with it?

Rust's macros on the other hand are excellent, and more languages should have expressive macros like that.

replies(2): >>44013927 #>>44017830 #
81. bsaul ◴[] No.44012800{3}[source]
i indeed got the feeling that rust style favors macros over complex function signatures.

I'm still unsure if it's a good thing in general, because as a general rule meta programming is always harder to debug. But for simple macros it seems like a nice trick.

82. Kwpolska ◴[] No.44012858{7}[source]
None of those arguments are convincing. In many cases, you can't handle errors more reasonably than just crashing or telling the user something went wrong. Java has RuntimeExceptions, which do not have to be declared in the function signature. Division by zero, or trying to index an array out of bounds, and the dreaded NullPointerException, are some examples of RuntimeExceptions.
replies(1): >>44014105 #
83. lenkite ◴[] No.44012899{4}[source]
Tip of the Day: You can wrap errors using "fmt.Errorf" in Go - this gives you a nice chain of error messages which is a pseudo stack trace. Another tip is to define and leverage sentinel errors when wrapping errors so you can test using "errors.Is" higher up when doing error handling in the call-hierarchy - like when mapping to http errors or exit codes.

Using "fmt.Errorf" is lean and painless compared to defining custom errors.

replies(2): >>44013044 #>>44013120 #
84. dontlaugh ◴[] No.44013044{5}[source]
It’s still not stack traces for every error, which is the default in every other language with stack unwinding.

In practice you have to use a combination of error wrapping and custom stack trace errors for your production logs to be useful on failure. The stdlib errors really should have stack traces.

85. homebrewer ◴[] No.44013068{5}[source]
Sure. Here's just one example I was able to quickly find in the browser history:

https://www.theregister.com/2025/05/09/users_advised_to_revi...

You can easily just not use the Oracle JDK, though, unless you're running commercial software which requires running on the Oracle runtime to get technical support.

As others have said, the problem is not the runtime, but libraries: many major .NET libraries have been going fully commercial, you can't really trust the ecosystem anymore.

86. homebrewer ◴[] No.44013120{5}[source]
And be very careful in constructing your error messages so that grepping the error string does not return you 50 matches. It's not the same as getting the error message + file name + line number.

This is the whole story of Go, they pick something established and reimplement a heavily cut down version of it for "reasons", then slowly catch up to competition over the next decade or so.

87. mrkeen ◴[] No.44013364{7}[source]
> You have no compiler-enforced way to know what exceptions might be thrown from a method or library.

Always assume exceptions will be thrown from a method or library.

replies(1): >>44014097 #
88. bitwize ◴[] No.44013927{4}[source]
> Why do I need to? Why can't I let the garbage collector deal with it?

Determinism.

With Rust lifetimes, you can statically prove when resources will be released. Garbage collectors provide no such guarantees. It has been shown that garbage-collected languages have a space-performance tradeoff: you need five times as much RAM to achieve the same performance, even with a "good" GC, as the same program with explicit memory management:

https://people.cs.umass.edu/~emery/pubs/gcvsmalloc.pdf

replies(1): >>44020196 #
89. bitwize ◴[] No.44013990{3}[source]
The founding document of GNOME was written by de Icaza. It was called "Let's Make Unix Not Suck", and his proposal for not sucking was basically to import much of the design of Windows and specifically COM into Linux as open source.

https://web.archive.org/web/20000815075927/http://www.helixc...

Miguel de Icaza has been stanning for Microsoft technologies, literally since the nineties.

90. breadwinner ◴[] No.44014097{8}[source]
Which ones? That's the issue.
91. breadwinner ◴[] No.44014105{8}[source]
What about the ones you can recover from? You don't want to crash the entire application every time there's an exception!
replies(1): >>44015271 #
92. Squeeeez ◴[] No.44014257{5}[source]
> Microsoft isn't thought of as evil anymore

Maybe you don't think of it like that, but please remember that, especially with some decisions made in the past months, more and more people are turning away from everything Microsoft.

93. kllrnohj ◴[] No.44014344{4}[source]
Java itself has basically abandoned the idea, or at least the ecosystem at large has. Checked exceptions are a decent idea but Java's implementation sucks. It needs to be a lot more automatic or something
replies(1): >>44015507 #
94. Kwpolska ◴[] No.44015271{9}[source]
You usually wouldn’t crash the entire application, the request that causes the issue will return a 500 error. (Or equivalents for non-web environments.)
replies(1): >>44016587 #
95. breadwinner ◴[] No.44015507{5}[source]
> Java itself has basically abandoned the idea

Some Java developers may have, due mostly to misinformation from the .NET camp.

96. tester756 ◴[] No.44015516{3}[source]
>It barely works on non MS platforms

What a bullshit

I've been running C# on Linux on production web apps backends since 2018 and had no issues

97. breadwinner ◴[] No.44015573{4}[source]
Side effects are not allowed in functional style programming. Exceptions are side effects, it doesn't matter whether they are checked or unchecked exceptions.

https://jessewarden.com/2021/07/why-functional-programmers-a...

While composing methods in stream style is convenient, methods that can throw exceptions warrant more careful coding, so convenience should not always be the priority.

replies(1): >>44020229 #
98. asa400 ◴[] No.44015981{4}[source]
Rust is genuinely novel, so for most of us its going to be less familiar, so it will take longer to learn. It's not like learning Python if you already know Ruby, for example. The upside is that it offers some pretty great guarantees you can't (yet) find elsewhere. If you want those guarantees it will be worth it, if those guarantees don't matter to you, it may feel like a slog.
replies(1): >>44016595 #
99. wpollock ◴[] No.44016315{6}[source]
Not sure, but the API docs say 1.2. Maybe the com.sun packages include their own implementation of TLS, separate from the TLS used in the rest of the JRE.
100. breadwinner ◴[] No.44016587{10}[source]
Some exceptions are not recoverable and may cause 500 error. Others such as FileNotFound are recoverable, for example by reading the file from an alternate location.
101. johnisgood ◴[] No.44016595{5}[source]
What guarantees are you speaking of exactly?
replies(1): >>44017351 #
102. asa400 ◴[] No.44017351{6}[source]
The banner one is memory safety without garbage collection. This enables is a terrific degree of library composition. Unlike C/C++, you can generally expect that libraries you pull in will also be memory safe.

The other one is thread safety, due to the compiler-enforced ownership semantics that prevent threads from accessing shared data unless they do so in a well-defined way.

103. treyd ◴[] No.44017830{4}[source]
You need lifetimes in order to make the affine types ergonomic to use through borrowings and aliasing mechanics. You want the affine types to prove all kinds of things about resource management, beyond just freeing allocations, which we generally call RAII. You model file and network handles as RAII, mutex locks as RAII.

Other languages use constructs like context managers or try-with-resources to capture this, but these constructs are very limited and make it very hard or impossible for these resource types to be put into a container and passed between threads. In Rust this is trivial and actually just works.

Garbage collectors usually give much weaker guarantees about when objects are freed, so destructors (which are sometimes not even available, like in JS) might only be called much later. You can't rely on a GC to unlock a muted for you. But in Rust it happens when the guard is dropped, always, immediately after it's last needed.

replies(1): >>44024056 #
104. int_19h ◴[] No.44020196{5}[source]
That's a paper from 20 years ago. GCs have improved since then.
105. int_19h ◴[] No.44020211{5}[source]
Funny you should mention open source and VSCode.

VSCode itself, for all the promotional materials about how it's open source, is officially "a distribution of the Code - OSS repository with Microsoft-specific customizations released under a traditional Microsoft product license".

But wait, you might say, it's just like Chrome vs Chromium - so long as we have the OSS edition, it's all good! But, unless you're writing JS or TS, you need extensions to do anything useful. Python is an extension. So is C#, and C++. And all of these are partially closed source - e.g. code completion for all three, or debugging for both C# and C++.

Worse yet, the licenses for those closed source parts specifically prohibit their installation and use in anything other than the official closed source VSCode distro. And this isn't just verbiage - there are actual runtime checks in all these products that block attempts to use them in VSCodium, Cursor etc.

The same goes for the official VSCode extension gallery / marketplace - you can't legally use it from anything other than the official VSCode. Enforcing that is trickier, but even here Microsoft managed to find a way to frustrate its users: it used to be possible to download a .vsix from the Marketplace, but that feature has been removed recently, precisely because people were using that in conjunction with Cursor etc.

Much open source, indeed.

106. int_19h ◴[] No.44020224{4}[source]
In fact, Java 1.5 got generics in 2004, one year before C# 2.0 added them in 2005. OTOH C# 2.0 had anonymous delegates, although syntactically they were still very verbose (no type inference for arguments) and thus only marginally better than Java's anonymous classes.

It was really from 2007 on (.NET 3.5 / C# 3.0) that C# started to get major features at an ever increasing pace while Java significantly stagnated for quite a long time.

replies(2): >>44022410 #>>44096834 #
107. int_19h ◴[] No.44020229{5}[source]
Exceptions are not side effects since no state mutation occurs.

If you really want to see everything through the FP lens, exceptions are exactly like Result<T, E> with implicit do-notation.

108. int_19h ◴[] No.44020234{5}[source]
One of the original lambda proposals for Java - the one from Neal Gafter - also had checked exceptions.

The problem is that you then need a way to capture exception specifications as generic type parameters to properly propagate contracts, which complicates the type system quite a bit. Which is why Java ultimately went with the much simpler proposal that didn't even try to tackle this.

replies(1): >>44043517 #
109. int_19h ◴[] No.44020244{7}[source]
Even if you sincerely believe that OOP is an anti-pattern, surely it was firmly "baked into the language" in Java already? Kotlin is fundamentally a better Java, not a rebuttal to Java.
110. int_19h ◴[] No.44020250{3}[source]
This is unfortunately common across most ecosystems today. Such lengthy stack traces are routine in JS, and fairly common in C++.
111. hocuspocus ◴[] No.44022410{5}[source]
Thanks, my recollection of C# evolution was blurry, I stand corrected.

So really, Sun and Oracle could have definitely moved faster around Java 6 and 7, the Java 8 release took a long time given the feature set.

I feel that records could have come quicker, their implementation isn't exactly ground breaking. Avoiding the async/await route was a smart call though, and Loom could probably not have happened much earlier.

Valhalla is another can of worms entirely

112. bsaul ◴[] No.44024056{5}[source]
Since you're mentionning it : i was surprised to find no golang "context" equivalent in rust's axum. How do you manage cancelling a long-running operation triggered inside an http request handler when the connection closes ? is it via RAII ?
replies(1): >>44024526 #
113. treyd ◴[] No.44024526{6}[source]
Yeah due to the design rule that "futures do nothing unless polled", it's enough to simply drop them and it cancels the whole tree of work. That doesn't necessarily cancel other tasks that were spawned that the future might have been waiting on. That's really easy to build if it's needed, since we do have RAII. This is part of why Rust's futures compose really well in ways that goroutines just do not and JS promises struggle with.
replies(1): >>44027692 #
114. bsaul ◴[] No.44027692{7}[source]
Never thought of it like that. Super interesting, thanks.

Edit: isn't that the case for all "async" systems, in every language ?

115. vips7L ◴[] No.44043517{6}[source]
Yes I'm well aware, but that is uniquely a Java problem. With a sufficiently strong type system, like the one in Scala, you can easily make checked exceptions work across higher order functions.
116. kllrnohj ◴[] No.44096834{5}[source]
C#'s runtime got generics in 2005 whereas Java still just has compiler-only sugar. It's a rather significant difference in the quality of the implementation. Not just for things like reflection but also means List<T> ToArray actually can return T[] instead of Object[].

It also means the .NET ecosystem didn't need the inline+reified kludge that Kotlin came up with https://kotlinlang.org/docs/inline-functions.html#reified-ty...

117. blandflakes ◴[] No.44122725{3}[source]
We've been running .NET on linux for years, what are you talking about?