Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I've been writing code for 15 years (in Java, C++ and more recently JavaScript and Go). I have finally given up on exceptions and settled on return values (I particularly like Go's system). Exceptions, while theoretically superior, simply tempts even good programmers to just kick errors down the callstack. I prefer guard clauses [1].

[1] http://wiki.c2.com/?GuardClause



I like the functional approach with Try, basically the exception becomes part of the return type, and the code chooses to either return an exception or the actual value.

   fun doSomething(arg: X): Try<Y>
Since the return signature needs adjusting, this leads to developers very consciously making the choice to either handle the error in the function, therefore avoiding adjusting the return type, or let the caller deal with it if it isn't logical to handle the error there.


Is this the same as `Result<Y>` (where `Result` is a sum type that contains either a value of `Y` or an "Error")? I haven't see it called `Try` before.


It's a common name for the concept in Java, Scala, and to a lesser extend Javascript land. But it's just a name for a `T | Exception` type.


Why wouldn’t you want errors kicked down the call stack? There are only two types of exceptions in the grand scheme of things.

1. Things go wrong that are out of your control - network down, database down, etc.

2. Coding mistakes. Either in your code or input arguments.

In either case, why not let the end user decide how to handle the error? Sometimes it’s some type of retry pattern, others it just to have a big try catch block that logs the fatal error and alerts someone.


> network down, database down

These should be handled gracefully at each layer, and an appropriate error thrown to the layer above.


At the highest layer. I don’t want other layers to wrap the error and probably lose the original error and the stack trace.

If the system is database or network dependent, there is no graceful way to handle it automatically most of the time.


> probably lose the original error and the stack trace.

In particular, Python solves this by having a “raise … from” language construct:

  try:
      dangerous_operation()
  except DangerousException as e:
      raise ThisModulesException("Dangerous operation failed") from e
Then, the original exception (including its stack trace, etc.) is available as an attribute on the exception you caught.


I’m not really in love with that either. The equivalent paradigm in C# is using the “Inner Exception” what value did you add by wrapping the exception? The stack trace already has the line number and the method that caused the error all the way down the stack. In Python, the code is right there. You can just open up the Python file in a text editor and see everything.


> what value did you add by wrapping the exception?

By wrapping the exception, the user of “thismodule” can simply call it by writing

  try:
      thismodule.do_thing()
  except thismodule.ThisModulesException:
      logging.exception("Failed to do thing")
      othermodule.do_other_thing_instead()
And this user of “thismodule” is free from having to know that thismodule calls dangerous_operation() and/or raises DangerousException (which are probably both from a different module). This information will be shown automatically in the exception’s backtrace, including all line numbers of all wrapped exceptions, so it is not lost. But the code which uses the module is both shorter, simpler, and has less knowledge about internals of the module it is using.


How would that be any more helpful than just catching a generic exception and logging it and do_other_thing?


If a low-level module has an unexpected ValueError or ZeroDivisionError, I don’t want that to be inadvertently caught and hidden by my except clause. In general, I want truly unexpected errors to propagate to the top level and generate a proper crash.

Only in the case of code where I really need to do something if a specific operation fails, for whatever reason, do I use “except Exception:” or its even more catch-all variant, the bare “except:” clause. And even then, I very often just use it to log a message or send an e-mail, and re-raise the exception again afterwards.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: