Live data from Hacker News

Exceptions (2003)

joelonsoftware.com

71–80 of 93 posts

Re: Exceptions (2003)

#71

You can make methods easily return multiple values in C# with Tuples. They are very handy.

For extremely low values of "easily". C#'s lack of pattern matching and tuple syntax makes dealing with tuples in C# a complete pain in the ass. It's ugly, annoying, code, in general.

P.S. You can do this in pretty much any language. Just define pair, triplet, etc.

Re: Exceptions (2003)

#72
post #17

Everyone here seems to agree that "exceptions are for exceptional conditions". The problem is that when you get down to details, there is disagreement about what exactly is an "exceptional condition". e.g. - you are trying to open a file for reading. The file does not exist. Is this exceptional? That depends on context, but the function that opens the file, being in an independent library, is usually designed without…

It seems to be a pattern in Python APIs to allow the caller to decide if a given function call should throw an exception when an error occurs. For example,

    a = some_dict['nonexistent_key']
would raise a KeyError, while

    a = some_dict.get('nonexistent_key', 'DEFAULT')
would return 'DEFAULT' instead.

Re: Exceptions (2003)

#73
post #16
post #11

Earlier quoted context omitted.

Isn't that what exceptions are used for in any language?

Certainly not. In Python, for example, exceptions are used to handle exceptional conditions . Here's an example. Say I'm writing a function and I have a dictionary I need to access values from often. Now say that 85% of the time the key I need is in the dictionary, but 15% of the time it is not. I could do this: if key in my_dict: execute_operation(my_dict[key]) else: pass So that if the key is not in the dictionary…

Python also has a `StopIterationError` that is raised to signal the end of a `for` loop, which is not exceptional at all.

Re: Exceptions (2003)

#74

Earlier quoted context omitted.

50 error conditions in a 20-line function doesn't really sound even remotely realistic. Probably only 5 of the 20 lines are actually calling functions that might return errors, and in most cases, we don't care what type of error is being returned. So if we're talking about 5 error-checks in 20 lines, then yes, we absolutely should write code to address them from the start . I mean, I can understand not dealing with e…

Nonsense. Is the network up? Is the connection to SQL up? Did someone just turn off the SQL machine half way through the query? Can we find the server? Are there any rows? Did the SQL compile? Do I have rights on this table? Have you just terminated me as a result of a deadlock? Did you return a Null when I was expecting a value? Did you return a float when I was expecting an int? Did my value just overflow? Did you…

The Haskell solution is to decompose the problem into the pure and impure parts while using types to eliminate a number of these. Then you end up with general classes of exceptions which can be handled using pure Maybe types, perhaps, in various locations as appropriate.

Resource availability (Is the network up? Is the connection to SQL up? Can we find the server? Did I just try to access the session but some other idiot clear it?)

Incomplete response (Did someone just turn off the SQL machine half way through the query? Have you just terminated me as a result of a deadlock?)

Result semantics and types (Are there any rows? Did you return a Null when I was expecting a value? Did you return a float when I was expecting an int? Did my value just overflow? Did you just return 0 and I tried to use it in division?)

Translation to intermediate language (Did the SQL compile?)

Assumptions about remote state (Do I have rights on this table? Did I just try to call a method on an object that is in fact null?)

Smart code handles these all separately. It's crazy to try to bundle them all into one function.

Re: Exceptions (2003)

#75

Earlier quoted context omitted.

Vague value judgments ("only for exceptional conditions") are the inevitable result of a failure to reason. The semantic function of exceptions is just a way for summing additional values onto the return type of a function because it has results that are not contained within the primary type. In this way, they are a more general and better typed version of NULL (which has it's places - contrary to the modern dogma, t…

I'm really fond of the idea of Nullable types (where you have to unwrap them by checking if they're null or errors to get the value). I don't think any non-functional language other than Rust has attempted to work this into the way they work yet, but it strikes me as a powerful mechanism that could also allow for deferring error handling to the site most capable of dealing with it.

Scala has the Try [0] and Either [1] types that can be used to achieve this. However, I believe Scala also allows any type to be NULL. In Rust there is no NULL so you don't have to worry if a value is ever NULL unless it is explicitly wrapped in an option instance.

[0] http://www.scala-lang.org/api/current/index.html#scala.util.... [1] http://www.scala-lang.org/api/current/index.html#scala.util....

Re: Exceptions (2003)

#76
post #17

Everyone here seems to agree that "exceptions are for exceptional conditions". The problem is that when you get down to details, there is disagreement about what exactly is an "exceptional condition". e.g. - you are trying to open a file for reading. The file does not exist. Is this exceptional? That depends on context, but the function that opens the file, being in an independent library, is usually designed without…

Vague value judgments ("only for exceptional conditions") are the inevitable result of a failure to reason. The semantic function of exceptions is just a way for summing additional values onto the return type of a function because it has results that are not contained within the primary type. In this way, they are a more general and better typed version of NULL (which has it's places - contrary to the modern dogma, t…

Overall, very good comment. My one point of disagreement is where you dismiss sum types because they clutter your program. Since sum types are the correct answer to this problem in theory, it seems to me that we shouldn't move on from that answer due to problems with implementing it in practice. Further, I think that the problem has already been solved pretty well in languages like Haskell that support type classes (like Monad) which make it so that you no longer have to worry about the sum types except when explicitly working with them (e.g. pattern matching on Left and Right). There's still room for improvement on working with sum types in Haskell (e.g. nested sum types can be annoying), but it's the best solution I've seen so far. Joel even makes note of Haskell's solution being good in the article, but only briefly.

Re: Exceptions (2003)

#77
post #58

Earlier quoted context omitted.

Vague value judgments ("only for exceptional conditions") are the inevitable result of a failure to reason. The semantic function of exceptions is just a way for summing additional values onto the return type of a function because it has results that are not contained within the primary type. In this way, they are a more general and better typed version of NULL (which has it's places - contrary to the modern dogma, t…

B&D? edit: Oh. http://c2.com/cgi/wiki?BondageAndDisciplineLanguage ? Makes sense.

lol... I've always felt that way about Java

Re: Exceptions (2003)

#78

Earlier quoted context omitted.

Vague value judgments ("only for exceptional conditions") are the inevitable result of a failure to reason. The semantic function of exceptions is just a way for summing additional values onto the return type of a function because it has results that are not contained within the primary type. In this way, they are a more general and better typed version of NULL (which has it's places - contrary to the modern dogma, t…

Overall, very good comment. My one point of disagreement is where you dismiss sum types because they clutter your program. Since sum types are the correct answer to this problem in theory, it seems to me that we shouldn't move on from that answer due to problems with implementing it in practice. Further, I think that the problem has already been solved pretty well in languages like Haskell that support type classes (…

> My one point of disagreement is where you dismiss sum types because they clutter your program ...

> There's still room for improvement on working with sum types in Haskell (e.g. nested sum types can be annoying)

What I dismissed is requiring sum types to be nominal. For instance, imagine that Haskell used the Scheme/Java representation of objects (ie everything is basically a member of one sum type, discriminated on a machine word in the header). We could then do things like:

    type Foo  = Bar | Baz
    type Foo2 = Bar | Baz
Where Bar and Baz could be any type. Now both Foo and Foo2 are just different names for exact same thing, and in fact the names Foo/Foo2 are irrelevant when pattern matching the result of a function that's been declared to return (Bar | Baz). This philosophy is a bit different from Haskell in that it assumes that "everything is an object" (rather than the zero-overhead structs of Haskell), and it implies that every sum type defined this way can only contain one branch for each included type (without names, there's no way to differentiate them), but a merging of the semantics could definitely be hammered out.

I think Rust (being not quite formed yet) could benefit from taking a stab at this, having every sum discriminator be globally unique, and every non-sum type having an associated global tag that only gets prepended when it is promoted to being an anonymous branch of a sum. The immediate use I envision is being able to create ad-hoc type hierarchies that are descriptive rather than prescriptive.

Re: Exceptions (2003)

#79

Earlier quoted context omitted.

Vague value judgments ("only for exceptional conditions") are the inevitable result of a failure to reason. The semantic function of exceptions is just a way for summing additional values onto the return type of a function because it has results that are not contained within the primary type. In this way, they are a more general and better typed version of NULL (which has it's places - contrary to the modern dogma, t…

I'm really fond of the idea of Nullable types (where you have to unwrap them by checking if they're null or errors to get the value). I don't think any non-functional language other than Rust has attempted to work this into the way they work yet, but it strikes me as a powerful mechanism that could also allow for deferring error handling to the site most capable of dealing with it.

If you use Guava, you even get Optional for Java. It works pretty well, but is obviously something you have to work into your API and can't easily use after the fact.

Re: Exceptions (2003)

#80
> This is ugly and annoying but it's better than getting magic unexpected gotos sprinkled throughout your code at unpredictable places.

The proper analogy would be not GOTO, but COMEFROM, no? A catch block is basically a COMEFROM. :)

Post reply on HN