3 ms·
That... is a very long winded way to miss the point. If you want to see what throws "FooException", you need an IDE to do it, unless you have the whole Javadoc
by JulianMorrison 5y ago
That... is a very long winded way to miss the point.
If you want to see what throws "FooException", you need an IDE to do it, unless you have the whole Javadoc memorised. It might be a hundred lines up and in the middle of a foo().bar().baz() chained series of method calls.
And yes, you need to know. The same exception in two different places might mean two very different things in terms of what actually broke, or what you should do to recover or fail the task at hand. Because of this, bubbling up raw exceptions out of deeply nested functions is generally a code smell, because by the time they get to the top level of even a module, all you know is "it broke". Unless you did the donkey work of wrapping the raw exception in something with more semantic context. The same work you'd do in Go.
- joshuamorton 5y ago> If you want to see what throws "FooException", you need an IDE to do it, unless you have the whole Javadoc memorised. It might be a hundred lines up and in the middle of a foo().bar().baz() chained series of method calls. This isn't any different in Go. Tracing Exceptions is actually often easier since they have unique names that you can grep for, whereas err is always err, so your only recourse is to trace across the callstack. It's, like, actually worse. > Because of this, bubbling up raw exceptions out of deeply nested functions is generally a code smell, because by the time they get to the top level of even a module, all you know is "it broke". Unless you did the donkey work of wrapping the raw exception in something with more semantic context. What? If you have a decent root level error message, that + a stack trace is almost always enough, and is as good or better than what you get in go, since the errors are more structured. I'd generally consider the donkey work you're describing an antipattern. Adding extra context or re-raising an exception should be done only rarely, when you have confidence that the new context you're providing is more useful than the prior (and lots of good languages support exception chaining so you get all of the context from many errors, instead of just one string).
- simiones 5y ago> If you want to see what throws "FooException" That's a completely different problem, and one that is not unique to errors or exceptions. For any polymorphic type where you want to handle different variants differently, you need to know the possible variants, and this information is, by definition, not present in the code sending the polymorphic value. This is true whether you want to handle exceptions in C++, Optional t in Haskell, or interface values in Go. > Because of this, bubbling up raw exceptions out of deeply nested functions is generally a code smell, because by the time they get to the top level of even a module, all you know is "it broke". Unless you did the donkey work of wrapping the raw exception in something with more semantic context. Wrapping up exceptions at every function is a huge code smell. You sometimes want some level of wrapping, especially around module boundaries or in functions with highly relevant context along the way, but generally most functions along a call stack don't have anything to add to a lower level exception - especially if the lower-level library is well made. For example, fs.PathError in Go and System.IO.FileNotFoundException in C# include a way to programatically find the name of the file that was not found, so even if thrown from a low level, a higher level still has the most relevant context available without wrapping. In contrast, java.io.FileNotFoundException does not include this information unless you parse the error string, so you would have a reason to wrap it with more relevant context.