News for hackers. Stuff that matters.
Re:JEP draft: Exception handling in switch by ackfoobar on Friday April 19, 2024 @02:06PM (#40090110)
`Either<MyError, Foo> foo()` and `Foo foo() throws MyError` and are pretty much isomorphic.

https://github.com/Kotlin/KEEP/blob/master/proposals/stdlib/...

Re:JEP draft: Exception handling in switch by wbl on Friday April 19, 2024 @04:33PM (#40091620)
Throws are nonlocal control transfer, not types. The interaction with the rest of the language is entirely different.
Re:JEP draft: Exception handling in switch by ackfoobar on Friday April 19, 2024 @05:39PM (#40092217)
Rather than looking at what the (abstract) machine does

- throws are nonlocal control transfer

- result types are adding a tag to the data or error

Think about whether the two can express the same things, and whether one can be translated to another without losing information.

Re:JEP draft: Exception handling in switch by kaba0 on Saturday April 20, 2024 @03:29AM (#40095520)
How are throws non-local? They simply pop a stackframe, the exact same way as the `return` keyword. Then the calling method gets to decide what to do, exceptions just have an implicit “unwrap-or-return/bubble up”.

Exceptions are nothing like gotos/long-jumps, it’s just “pressing escape key once vs keeping it pressed”, basically.

Re:JEP draft: Exception handling in switch by mrkeen on Friday April 19, 2024 @03:55PM (#40091301)
> Foo foo() throws MyError

Why did you link to Kotlin? It doesn't appear to have `throws MyError`.

Re:JEP draft: Exception handling in switch by ackfoobar on Friday April 19, 2024 @04:05PM (#40091391)
You're right that was confusing.

For this line, where the same can be said for Java.

> Notice, that monad comprehension over Try monad is basically built into the Kotlin language.

Re:JEP draft: Exception handling in switch by bedobi on Friday April 19, 2024 @04:07PM (#40091413)
...which is even worse, because now the compiler is saying "when you call this, you will get this" and you have NO way of knowing that the call actually returns any number of exceptions, as a matter of course, without reading EVERY line called by EVERY line in that method

vs just plainly indicating you will get Either<Error, Foo>

Re:JEP draft: Exception handling in switch by wiseowise on Friday April 19, 2024 @06:24PM (#40092595)
What are you talking about? You literally can with sealed classes.
Re:JEP draft: Exception handling in switch by bedobi on Monday April 22, 2024 @04:30PM (#40119881)
I'm talking about the fact that every function call in Kotlin can return an exception (and it will always be undeclared, because Kotlin doesn't have checked exceptions)

this has nothing to do with sealed classes

Re:JEP draft: Exception handling in switch by bedobi on Friday April 19, 2024 @02:27PM (#40090361)
they're in no way even remotely similar

Unchecked exception

    Foo foo = myClass.getFoo() // throws exception, but doesn't tell you, and nor does the compiler
Checked exception

    // now we have to do this everywhere, in every layer, lol (or put throws)
    try { 
      myClass.getFoo()
    } catch(Exception e){ //now we're catching ALL exceptions, yay
      // do what? who knows
    }
Sane Either/Option

    Either<Error, Foo> foo = myClass.getFoo()
^^^ the compiler disallows treating this as a Foo - but also doesn't force us to handle anything, we can pass this around without handling anything, and only when we want to, call map, flatMap, fold etc

Notably, it's just an object, it doesn't require weird special syntax, halting execution, hierarchies of exception (which mean you catch too much or too little etc etc etc)

it's objectively better on every metric except familiarity

but since Java devs are stuck in the 90s and refuse to listen to even their own language designers about how traditional Java ways of doing things are fundamentally broken (don't take my word for it, go listen to what Josh Bloch and Brian Goetz say), they refuse to even consider adopting it until they're forced to (like when Java introduced Option) (which itself is the worst implementation of that type in any language, but that's a side note)

Re:JEP draft: Exception handling in switch by lolinder on Friday April 19, 2024 @03:32PM (#40091085)
> it's objectively better on every metric except familiarity

I find that as a rule, people who throw around the word "objectively" a lot tend to dramatically overestimate how much they know, usually because they're advanced beginners in the domain that they're so confident in and haven't yet run into the complexity of real-world implementation.

There are very few features in programming languages that can be described as "objectively" better or worse than other features. The whole discipline is more art than science, and the only practitioners who actually accomplish anything meaningful in their work are the ones who acknowledge the inherent subjectivity of most of programming language design.

Programming languages do not exist in a vacuum, they exist in an ecosystem of interrelated individuals and technology. Do not be so quick to dismiss the weight of generations of programmers and billions of lines of code as people just being "stuck in the 90s".

Re:JEP draft: Exception handling in switch by mrkeen on Friday April 19, 2024 @03:55PM (#40091297)
> the weight of generations of programmers and billions of lines of code as people just being "stuck in the 90s".

The weight of generations of programmers and billions of lines of code have come to the conclusion that:

1) Functions should declare/document what they return (including failure modes)

2) Checked exceptions are not worth it

These are mutually exclusive.

Result types genuinely are better.

Re:JEP draft: Exception handling in switch by lolinder on Friday April 19, 2024 @04:04PM (#40091383)
I'm a fan of Result types in languages that incorporated them as part of the design (say, Rust), but I'm not convinced that they're the right answer for Java. Result types only really make sense in a highly expression-oriented language, and while Java has taken important steps in that direction it's really not there yet and likely never will be.

I think a more fruitful path of exploration is improvements to the ergonomics of checked exceptions. Semantically there really is very little difference between checked exceptions and Result types (OP's argument to the contrary is a straw man and proves nothing), the main reason why people fought their use in the early days of Java was that the ergonomics were pretty poor. I suspect that there are a few tweaks that could be made with modern PL techniques that would make checked exceptions feel more comfortable (analogous to the addition of ? in Rust), and since they've been baked into the language for decades now through thousands of existing libraries the returns from making them easier to use will be much higher than the returns from creating a new "standard" way to handle errors in Java.

Re:JEP draft: Exception handling in switch by wiseowise on Friday April 19, 2024 @06:17PM (#40092535)
> Result types genuinely are better.

Only if there’s a language support for them. Like Rust, where I can concisely drop out of function if it’s an error case.

https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/sh...

If I have to constantly write

when(result) { Ok -> … Err -> … }

then checked exceptions and throws clause are better.

Re:JEP draft: Exception handling in switch by bedobi on Friday April 19, 2024 @04:12PM (#40091447)
Normally I'd agree with you, but this really is not a matter of opinion, it's up there with eg compiler built in null safety is superior to everything being able to be null. There's plenty of people who think it's not, and who come up with all kinds of bizarre reasons to say so, it's just that they're wrong. Even the Java language designers themselves (including people like Bloch and Goetz) have for decades been talking and writing books about how wrong Java got a lot of things. But the community is stuck in its ways and wants to keep coding like they always have.
Re:JEP draft: Exception handling in switch by lolinder on Friday April 19, 2024 @04:23PM (#40091539)
> Even the Java language designers themselves (including people like Bloch and Goetz) have for decades been talking and writing books about how wrong Java got a lot of things.

Can you please provide a citation, with a link, for where Bloch or Goetz said that checked exceptions are something Java got wrong?

Effective Java has a lot to say about exceptions, and does encourage avoiding them where possible, but you have to really be cherry picking to read it as Bloch saying to never use checked exceptions when the 2017 edition says this:

> The cardinal rule in deciding whether to use a checked or an unchecked exception is this: use checked exceptions for conditions from which the caller can reasonably be expected to recover. By throwing a checked exception, you force the caller to handle the exception in a catch clause or to propagate it outward. Each checked exception that a method is declared to throw is therefore a potent indication to the API user that the associated condition is a possible outcome of invoking the method.

And this:

> Many Java programmers dislike checked exceptions, but used properly, they can improve APIs and programs. Unlike return codes and unchecked exceptions, they force programmers to deal with problems, enhancing reliability.

Bloch advocates against overuse of exceptions, which is fair, but his reasoning applies equally well to overuse of Result: if you can use the type system to prevent an invalid value from being passed in, do so!

As for Goetz, this is the most recent commentary I can find from him on checked exceptions [0]:

> If people are not forced to document what errors their code throws, they won't -- especially the people that the people in camp (3) are afraid of. As long as those folks are allowed to code, the value we get from checked exceptions forcing developers to document failure modes overwhelms the annoyances.

> But, as I said above, I think many of the annoyances can be removed by adding a small number of exception streamlining constructs. This doesn't help Neal with simplifying closures, but it does help us get our job done with a little less pain.

> Finally, a meta-note -- its really easy to misinterpret the volume of support for "removing checked exceptions" as any sort of gauge for community consensus. We're intimately familiar with the pain that checked exceptions cause; we're substantially less familiar with the pain that they free us from. (Obviously, neither approach is perfect, otherwise there'd be no debate.)

[0] https://nofluffjuststuff.com/blog/brian_goetz/2007/06/remove...

Re:JEP draft: Exception handling in switch by bedobi on Friday April 19, 2024 @04:42PM (#40091678)
I was referring to how Bloch and Goetz have tried to tell Java devs for decades to avoid null, mutation and just in general the traditional Java coding style, with little success.

As for exceptions, it's hilarious that they and the Java community in general are still stuck on arguing about checked vs unchecked exceptions. That's my whole point - you can argue about that forever and you will always be wrong, because exceptions are a failed experiment and will never work or reach a consensus. Move past it.

I really, REALLY don't understand what's so wrong with just.. making methods return what they say they do, as plain objects. The antagonism towards doing so is literally just ingrained culture with zero sensible justification.

Re:JEP draft: Exception handling in switch by lolinder on Friday April 19, 2024 @04:54PM (#40091785)
> I really, REALLY don't understand what's so wrong with just.. making methods return what they say they do, as plain objects. The antagonism towards doing so is literally just ingrained culture with zero sensible justification.

From experience with Rust, a major concern is lack of stack traces in error messages. Rust has libraries that built up around Result to address this (like Anyhow), but by the time you've included Anyhow with stack traces enabled you've basically reinvented checked exceptions in Result types. For Rust this is probably more ergonomic than introducing exceptions into the language, but Java already has checked exceptions that work like Anyhow does out of the box.

That's the part that you seem to be missing: checked exceptions are nearly identical semantically to result types, and they already exist and are widely used in the language. There are a few minor ergonomic problems that discourage their wider use, but it makes way more sense for Java to fix those than for people to migrate to Result types, which are not nearly as ergonomic in Java as they are in Rust or similar.

You tried to address this upthread, but it was frankly a pretty bad strawman that contrasted a horrible example of checked exceptions with an idealized example of a Result type. You then compounded the strawman with a double standard by demanding that the checked exceptions example immediately account for what would happen in the catch block while hand-waving away the handling of Left values in the Result type as something that we can deal with at some unspecified later time.

Dealing with failures later at the edges of the system is the reason why exceptions were invented, so it's highly disingenuous to claim that as a capability unique to Result types.

Re:JEP draft: Exception handling in switch by ackfoobar on Friday April 19, 2024 @05:16PM (#40091997)
> by the time you've included Anyhow with stack traces enabled you've basically reinvented checked exceptions in Result types

If the error is wrapped into an `anyhow::Error`, and the result in `anyhow::Result<T>`, it has less information than checked exceptions. Coz the type now only tells you the call can fail, but not how (it's anyhow!).

> That's the part that you seem to be missing: checked exceptions are nearly identical semantically to result types

That's the crux.

Re:JEP draft: Exception handling in switch by ackfoobar on Friday April 19, 2024 @02:39PM (#40090492)
> it doesn't require weird special syntax

But to use that `Either` object ergonomically languages do add extra syntax. Rust with the `?` and Scala with the for-comprehension.

The interactions with HOFs are also subtle.

In Rust it requires a smart use of type params for `collect`.

In Scala (following the Haskell tradition) there's the pair of coloured functions `traverse` and `map`. But `traverse` is only available in third-party functional programming libraries (Cats or Scalaz). So out of the box there isn't a way to cleanly `map` a function that returns the `Either` type.

> now we're catching ALL exceptions, yay

Your example is not that different from upcasting.

    val foo: Either<Exception, Foo> = // Either is covariant
      myClass.getFoo() // returns Either<MyError, Foo>
Re:JEP draft: Exception handling in switch by bedobi on Friday April 19, 2024 @04:18PM (#40091496)
the difference is that in catch (Error e), you're actually catching the Error type, the superclass of exception

in Either<Error, Foo>, Error is not that, it's usually a sealed class with the full range of things that can go wrong with that thing, no more, no less, eg

    sealed class ChangePasswordErrors {
        class NotAuthenticated() : ChangePasswordErrors()
        class ChangePasswordError() : ChangePasswordErrors()
        class ReusedPassword() : ChangePasswordErrors()
    }
or whatever (but I could have made that clearer)

and no, languages should not add weird syntax to deal with Either, Option, Try etc. Just call map, flatMap, fold.

Re:JEP draft: Exception handling in switch by ackfoobar on Friday April 19, 2024 @04:26PM (#40091563)
> the Error type, the superclass of exception

> in Either<Error, Foo>, Error is not that

This is confusing. On the JVM `Error` and `Exception` are both subclasses of `Throwable`. If you meant a specific error type, give it another name. (I used `MyError` in the comments above.)

> sealed class ChangePasswordErrors {

If some kind of failure has to be handled, In Kotlin you can write:

    sealed class ChangePasswordResponse {
        class Success(): ChangePasswordResponse()
        class NotAuthenticated() : ChangePasswordResponse()
        ...
> Just call map, flatMap, fold.

Enjoy your callback hell.

Re:JEP draft: Exception handling in switch by bedobi on Monday April 22, 2024 @04:32PM (#40119914)
Good poit re using MyError, that is indeed what I meant in the Either case

Not sure what you mean by callback hell, I've never experienced that using Either. I just pass the Either from the db layer or whatever to the resource layer or whatever, where I fold it into a 200 with the requested thing or a 4xx or 5xx or whatever depending on the MyError (which is usually a sealed class that lists everything that can go wrong with the thing)

Re:JEP draft: Exception handling in switch by vips7L on Friday April 19, 2024 @02:51PM (#40090655)
Odersky actually hates Either and refuses to use it in Scala for errors.
Re:JEP draft: Exception handling in switch by mrkeen on Friday April 19, 2024 @03:57PM (#40091318)
I find this extremely weird.

Why even use Scala then? Is it just for the implicits and long build times?

Re:JEP draft: Exception handling in switch by kaba0 on Friday April 19, 2024 @04:04PM (#40091385)
> or put throws everywhere

Or put Result/Error etc. type everywhere. It’s literally analogous.

Re:JEP draft: Exception handling in switch by bedobi on Friday April 19, 2024 @04:20PM (#40091511)
it's literally not, I'm sorry but folks who think this (and it's extremely common) simply haven't familiarized themselves with the differences
Re:JEP draft: Exception handling in switch by kaba0 on Friday April 19, 2024 @04:35PM (#40091632)
Repeating the same stuff won’t make it true.
Re:JEP draft: Exception handling in switch by ackfoobar on Friday April 19, 2024 @04:53PM (#40091781)
Going off-topic, I've been on both sides of seeing the analogousness or not. I think there might be two modes of thinking for deciding whether two things are analogous.

Mathy people inspect the "shape" of, and interactions within the two things. Other people go for intuitive vibes. For more concrete stuff the two modes of thinking usually match up.

Summation and multiplication are literally different things, but a lesson in group theory tells you (ℝ, +) and (ℝ⁺, ×) are isomorphic.

Re:JEP draft: Exception handling in switch by kaba0 on Saturday April 20, 2024 @03:23AM (#40095508)
Something being analogous to something else means, that there is a bidirectional function/mapping between the two.

One can easily map any Result-typed expression to a checked exception one, and vice versa.

Re:JEP draft: Exception handling in switch by wiseowise on Friday April 19, 2024 @06:13PM (#40092497)
> Objectively better

> is forced to pattern match on every interaction with object now

Not sure if trolling.

Re:JEP draft: Exception handling in switch by vips7L on Friday April 19, 2024 @07:19PM (#40092959)
I watched a video from Martin Odersky (creator of Scala) and he was sort of the same opinion when talking about effects.

    A b() throws C;

And

    A b()(using Throws[C]); 

are exactly the same.
Re:JEP draft: Exception handling in switch by kelnos on Saturday April 20, 2024 @01:05AM (#40094960)
Ignoring the much more annoying syntax needed to handle exceptions, I'd agree. But that's not the problem: you never know if foo() throws things other thank MyError. So in practice you still need an exception catch-all after your catch for MyError.

After years of Java, handling errors in idiomatic Scala with Either was a breath of fresh air. (And you can even wrap things that might throw in Try() to save yourself the headache of a try/catch tree.) Rust did it right with Result, and leaving out exceptions entirely.