Hacker News new | ask | show | jobs
by bobogei81123 252 days ago
This is just AI trying to tell us how bad we designed our programming languages to be when exceptions can be thrown pretty much anywhere
1 comments

So you think java's checked exceptions are a better model? No opinion myself, but that way seems widely considered bad too.
> So you think java's checked exceptions are a better model?

Checked Exceptions are a good concept which just needed more syntactic-sugar. (Like easily specifying that one kind of exception should be wrapped into another.) The badness is not in the logic but in the ecology, the ways that junior/lazy developers are incentivized to take horrible shortcuts.

Checked exceptions are fundamentally the same as managing the types of return-values... except the language doesn't permit the same horrible-shortcuts for people to abuse.

Meme reaction: http://imgur.com/iYE5nLA

_____

Prior discussion: https://news.ycombinator.com/item?id=42946597

Why do you need exceptions at all? They’re just a different return types in disguise…

Also, division by zero should return Inf

> Why do you need exceptions at all? They’re just a different return types in disguise…

You don’t need exceptions, and they can be replaced by more intricate return types.

OTOH, for the intended use case for signalling conditions that most code directly calling a function does not expect and cannot do anything about, unchecked exceptions reduce code clutter (checked exceptions are isomorphic to "more intricate return types"), at the expense of making the potential error cases less visible.

Whether this tradeoff is a net benefit is somewhat subjective and, IMO, highly situational. but if (unchecked) exceptions are available, you can always convert any encountered in your code into return values by way of handlers (and conversely you can also do the opposite), whereas if they aren’t available, you have no choice.

Correct, but that's not how I think about systems.

Most problems stem from poor PL semantics[1] and badly designed stdlibs/APIs.

For exogenous errors, Let It Crash, and let the layer above deal with it, i.e., Erlang/OTP-style.

For endogenous errors, simply use control flow based on return values/types (or algebraic type systems with exhaustive type checking). For simple cases, something like Railway Oriented Programming.

---

1. division by zero in Julia:

  julia> 1 / 0
  Inf
  
  julia> 0 / 0
  NaN
  
  julia> -1 / 0
  -Inf
> division by zero should return Inf

Sometimes yes, sometimes no?

It's a domain specific answer, even ignoring the 0/0 case.

And also even ignoring the "which side of the limit are you coming from?" where "a" and/or "b" might be negative. (Is it positive infinity or negative infinity? The sign of "a" alone doesn't tell you the answer)

Because sometimes the question is like "how many things per box if there's N boxes"? Your answer isn't infinity, it's an invalid answer altogether.

The limit of 1/x or -1/x might be infinity (or negative infinity), and in some cases that might be what you want. But sometimes it's not.

Division by zero is mathematically undefined. So two's complement integer division by zero is always undefined.

For floating point there is the interesting property that 0 is signed due to its signed magnitude representation. Mathematically 0 is not signed but in floating point signed magnitude representation, "+0" is equivalent to lim x->0+ x and "-0" is equivalent to lim x->0- x.

This is the only situation where a floating point division by "zero" makes mathematical sense, where a finite number divided by a signed zero will return a signed +/-Inf, and a 0/0 will return a NaN.

Why should 0/0 return a NaN instead of Inf? Because lim x->0 4x/x = 4, NOT Inf.

OK, but I think it's not up to the programming language designers to define mathematical properties of the operations on specific data types.

I think the most pragmatic solution is to have 2 tiers:

1. use existing standards (i.e. IEEE 754 for FP, de-facto standards for integers, like two's complement, Big-Endian, etc.)

2. fast, native format per each compute device, using different sub-types so you will not be able to mix them in the same expression

Or -Inf, depending on the sign of the zero, which might catch some programmers by surprise, but is of course the correct thing to do.

  a/0 =  Inf when a>0
  a/0 = -Inf when a<0
  a/0 =  NaN when a=0
No this doesn't work either

In the context of say a/-0.001, a/-0.00000001, a/-0.0000000001, a/<negative minimum epsilon for denormalized floating point>, a/0

Then a/0 is negative when a>0, and positive when a<0

Why not just to use IEEE 754?

> According to the IEEE 754 standard, floating-point division by zero is not an error but results in special values: positive infinity, negative infinity, or Not a Number (NaN). The specific result depends on the numerator

What about division of zero by zero?
Unchecked exceptions are more like a shutdown event, which can be intercepted at any point along the call stack, which is useful and not like a return type.
Why do you need the call stack at all?
Debugging. It's one of the most useful tools for narrowing down where an error is coming from and by far the biggest negative of Rust's Result-type error handling in my experience (panics can of course give a callstack but because of the value-based error being most commonly used this often is far away from the actual error).

(it is in principle possible to construct such a stack, potentially with more context, with a Result type, but I don't know of any way to do so that doesn't sacrifice a lot of performance because you're doing all the book-keeping even on caught errors where you don't use that information)

Call Stack isn't a zero-cost abstraction, it makes threads more heavy-weight than they should be.

If you only need it for debugging, then maybe better instrumentation and observability is the answer.