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

No. If you want to have the same information without that approach, you need to implement nested levels of error information, ten layers deep, each layer having a diagnostic, a log level, and file, line information, and a reason. In other words, you are building your own custom stack trace object, with annotated diagnostics. In addition, you can't reason about this at the upper level anyway, or you are rebuilding your application stack at some other place. The only thing you can do is to unwrap that object and serialize it into a diagnostic, which you could have done with less code, less memory and less compute. In addition, you would need to allocate on a failure path, which sounds like a nightmare.

Also you can never have comments in the code, because what you would write into the comment is now in the diagnostic itself. That means you can also turn on DEBUG_LEVEL_XXL and get a nice description, what the program actually does and why.

> This is error-prone

Why is it error-prune. You receive an error of one type and need to convert it into an error of another type. You need to handle that or the compiler will bark.

> obscures the logs

How does it obscure the logs?



The information about what the code was doing at the time an exception is thrown is implicit in the stack trace; the control flow is evident from line numbers, and with half-decent tooling, you can get hyperlinks from stack traces to source code.

If there's extra information you need to know about what's going on, you probably want to log it in non-error cases too, because it'll contextualize an error before the error occurs. You can't log on unwind for decisions made in routines that were invoked and returned from, and are no longer on the stack, in the error case.

> Why is it error-prune. You receive an error of one type and need to convert it into an error of another type. You need to handle that or the compiler will bark.

This is a choice you've made, not something inherent to unchecked exceptions. My argument is that you should not generally change the type of the error, and let polymorphism cover the abstraction. It's error prone because it's code you need to write. All code you write is on the cost side of the ledger and liable to contain mistakes.

> How does it obscure the logs?

You get lots of error log messages for a single error.

> In addition, you would need to allocate on a failure path, which sounds like a nightmare.

I wanted to address this separately. Allocating on the failure path is only a real problem in OOM or severe memory corruption scenarios, in which case logging may not be available and the best course of action is probably to abort the program. In this case, you want the logs that led up to the abort, rather than trying to log positively under severe memory conditions.

Are you a Go user by any chance? I've stayed away from Go precisely because of its boilerplate-reliant error handling mechanism. Or if you're stuck with C++ (with or without exceptions), my commiserations.


> The information about what the code was doing at the time an exception is thrown is implicit in the stack trace; the control flow is evident from line numbers, and with half-decent tooling, you can get hyperlinks from stack traces to source code.

Yes, but when you want to generate a diagnostic from that stack trace, you basically need to branch on all the possible internal states of internal layers at the point you catch the exception. So either you are incapable of generating detailed diagnostics or you essentially model the whole behaviour in a single place. Also the point where the error messages are generated is now completely removed from the place where the error did occur. This sounds like a nightmare to maintain and also means that the possible error messages aren't there as documentation when reading the code.

> If there's extra information you need to know about what's going on, you probably want to log it in non-error cases too, because it'll contextualize an error before the error occurs. You can't log on unwind for decisions made in routines that were invoked and returned from, and are no longer on the stack, in the error case.

I never said, that you can only use this for error cases. In fact what is an error and what not, is not defined in a single layer. For example a failure to open a file will be an error in a lower layer, but for an upper layer, that just means that it should try a different backend. Or a parsing error is fatal for the parser, but it might mean that the file format is different and the next parser should be tried, or that the data is from the network and can simply be discarded. An empty field can be normal data for the parser, but for the upper layer it is a fatal error.

> This is a choice you've made, not something inherent to unchecked exceptions. My argument is that you should not generally change the type of the error, and let polymorphism cover the abstraction.

Then either the reported errors are completely unspecific and unactionable or you are leaking implementation details. When you want to handle every error specifically and not leak implementation details, you need to handle it locally. When you want to know that you handle all cases, unchecked exceptions are unsatisfying. In my opinion programs should know what is going on and not just say "my bad, something happened I can't continue". That does not lead to robust systems and is neither suitable for user transparency nor for automated systems.

In my opinion software should either work completely automated or report to the end user. Software that needs sysadmins and operators at runtime is bad. That doesn't mean that that never occurs, it will, but it should be treated as a software defect.

> You get lots of error log messages for a single error.

Yes, but this describes the issue at different layers of the abstraction and in my eyes the whole thing is a single error message. Neither the fact that resource X isn't available nor the fact that some connection was refused, is a complete error description in isolation. You need both for a (complete) error description.

> Allocating on the failure path is only a real problem in OOM

Yes, but first I don't like my program to act bad in that case, and second, it is also a nightmare for predictable ownership semantics. I generally let the caller allocate the error/status information.

> Are you a Go user by any chance?

I have never used Go, not even tried, but what I read about the error mechanism appealed to me, because it matches what I think is a good idea and do anyway.

> Or if you're stuck with C++ (with or without exceptions), my commiserations.

I don't feel that unhappy with that approach. I think this is a design and architectural decision rather than a language issue.

> with or without exceptions

Currently, definitely without, because it isn't even available when targeting a free-standing implementation (embedded), but I also don't prefer them, it makes for unpredictable control flow and makes it hard to reason about sound-, complete- and exhaustiveness.

You seem to have the impression, that you can just panic and throw a stacktrace. That might work fine for a program running in the terminal and targeting developers, but it is not acceptable for end users nor for libraries. I also know programs that just output a stacktrace and crash. That is stupid. I mean I understand what is going on, because I am a developer, but first I am not familiar with every codebase I use, and second the average end user is not able to act on any of that and will be angry for good reason when it's documents are gone, data is corrupted or even the workflow is interrupted. I also don't perceive a network error, a file (system) issue or OOM to that rare for it to be acceptable, to just ignore it. I should be part of normal program behaviour.




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

Search: