Just because it's useful to wrap exceptions in 5% of the cases doesn't mean you should wrap the rest 95% just in case. YAGNI.
1. You don't know which exceptions will be raised in advance. Anything that involves IO can fail in a plethora of ways, and you don't even know which calls involve IO (e.g. a library might choose to cache something on disk).
2. Consumers of your code will not know how to deal with those exceptions.
3. Most of exceptions are unrecoverable (that's why they are called exceptions), the best course of action is to crash, which happens by default.
4. You debug those exceptions by looking at stack trace. Adding extra levels just to give a fancy meaningless name to an exception does not help.
5. The whole point of exceptions is to propagate. Parthenon essentially suggests converting exceptions into return values.
I think there are two kinds of exceptions in languages that have them as their error handling mechanism.
1. Exceptions you expect your consumers to handle.
2. Exceptions you don't expect your consumers to handle.
The first one I would argue you should wrap third party exceptions. There is in nearly every case important context in your code that the thrower of the third party exception will not know and whoever is reading or handling the exception will want to know.
The second one should do whatever the equivalent of crashing is for your use case. Either exiting the program hard or bubbling to some top level handler where a "Something weird and unexpected happened and we can't continue whatever action was going on" alert or log get's recorded and the activity is terminated.
If something is throwing an exception and you don't know it can be throwing an exception it probably belongs in category 2. You may over time transition it to category 1 when you figure out that it is actually handleable.
In my experience though if you aren't disciplined then every exception ends up being lumped into category 2 whether it should be or not. Any language that helps you force the categorization gets bonus points from me.
1. You don't know which exceptions will be raised in advance. Anything that involves IO can fail in a plethora of ways, and you don't even know which calls involve IO (e.g. a library might choose to cache something on disk).
Not sure how this relates to the code design decision
2. Consumers of your code will not know how to deal with those exceptions.
Not sure the point here but if the argument is that the caller won't know how to deal with the third party exception, I disagree. Typically it just needs to return a single exception type that wraps any underlying cause. Caller can just either decide to deal with it or rethrow.
3. Most of exceptions are unrecoverable (that's why they are called exceptions), the best course of action is to crash, which happens by default.
If the database is unresponsive do you want to crash your program? I wouldn't. I'll usually retry until its available.
4. You debug those exceptions by looking at stack trace. Adding extra levels just to give a fancy meaningless name to an exception does not help.
Really don't think an extra trace in the stack is a reason to not create a well defined contract.
5. The whole point of exceptions is to propagate. Parthenon essentially suggests converting exceptions into return values.
I didn't see anywhere in the doc where they mentioned converting exception into return values. Wrapped exceptions are still exceptions.
6. It is much faster and easier to develop an application's "happy path" while completely ignoring failures. Failure handling is a software maintenance issue. YAGNI.
> You don't know which exceptions will be raised in advance.
> Most of exceptions are unrecoverable (that's why they are called exceptions), the best course of action is to crash, which happens by default.
I prefer to propagate such unknown exceptions to a top-level catch to clanly log that something happened.
I usually have two types of exceptions: the ones I expect at some point (a HTTP call failing for some reason) that I may ("when I have time") group in "known exceptions we should not worry about" and log them as "informational", and exceptions I did not anticipate that I want to log as well, but with a critical level because they were, well, unexpected.
So crashing right when they happen may not be the best strategy.
> Most of exceptions are unrecoverable (that's why they are called exceptions), the best course of action is to crash, which happens by default.
This is why D separates exceptions into two categories:
1. Error - these are not recoverable. The only reason to catch them is to maybe try to save some state or log a message or shut down the reactor before crashing.
2. Exception - these are recoverable
(I'm being facetious. Any system design where, while unwinding a fatal error, one relies on it to shut down the reactor is a horrible, terrible design.)
> Just because it's useful to wrap exceptions in 5% of the cases doesn't mean you should wrap the rest 95% just in case. YAGNI.
I'm going to curse here, but ... fucking seriously.
If I get a .net ADO exception coming out of a 3rd party library it's absolutely not going to shock me or throw me for a loop. But do you know what IS a pain in the ass? Using 3 different libraries, all that wrap that same ADO exception in their own custom exception.
"Just because it's useful to wrap exceptions in 5% of the cases doesn't mean you should wrap the rest 95% just in case"
Yes you should wrap ALL third party exceptions for the reasons given in the post. There is no 'just in case' reason. Any third party exception returned may cause your client to be dependent on it.
In short: if you have a meaningful recovery pathway for a particular exception this can be useful, but I found that 9 out of 10 exceptions/errors in code cannot be recovered from.
This is checked exceptions all over again... Frankly, I usually do exactly the opposite. Most cases I've seen the exceptions that can arise are not widely known in advance, and for most spots where exceptions can be raised from I simply do not care about them as much either. If my DB is not accessible - do I really, really need to wrap it? If the DNS resolver is failing - do I really really need to wrap it?
When debugging I actually appreciate when a library/controller/module/whatnot does not attempt to "enhance" the exception from something outside of its control with a wrapper, and value seeing the original failure instead.
> If my DB is not accessible - do I really, really need to wrap it?
I would say yes. Most of your code doesn't care that the database failed with a PG-300292-AB error. What you do care about is that there is a system failure. If you wrap your system failure, and document it as THE exception, the API caller will know what to look for.
In general there really are only a few exceptions: system, invalid input, non found. I'm probably missing one or two others. Most exceptions/errors are one of these. If you make these 3-5 exceptions/errors explicit, you're APIs will be pretty clear. A function can pretty much always returns a system exception (bad IO, sunspots, etc.). It can also throw an invalid input exception. Your client can handle these cases differently. For a HTTP RPC service, you return a 500 for system and 400 or 422 in the example. Not bad.
One can even wrap the exception with a more detailed message (which I know you said you didn't like) that preserves flow. So you get a invalid input exception, you can add details around the null value by properly nesting the messages. They should show up in your logs.
That is an interesting topic, as I went through the cycle of adding and removing them, they always felt like the right thing, but they felt like "work". They make coding less fun, having to think through failure conditions, like you say, you don't care about.
I guess the answer is really there is no one sized fits all solution, we have some applications where the correct solution is to crash, we have other applications that can't crash under any condition. You just need to know where you are.
Edit:
Also, out of the box C++ checked exceptions implemented terribly.
We primarily use custom exceptions to add detail to our error logs while still remaining concise. Generic exceptions are exceptionally useless for trying to debug issues, I've found.
I think this only makes sense if the 3rd party is also throwing custom exceptions.
If you want to reduce coupling you should avoid throwing custom exceptions at all. Semantic information can go in the error message and log. The error type should be used to indicate to your program whether an error is recoverable, retriable or some other action needs to be taken. For example google on has 16 canonical error codes for all APIs.
Addendum: write a useful exception message for future travelers that might need to review that stack.
What was null? Why might it be null? Mis-configuration? Missing configuration? Can't load some data or connect to some system? Which setting should be verified? Give simple hints on what might have gone wrong to help future you.
Amazes me that so many people dont understand that if all things are dependant upon your business logic or on your domain then this is natural. Its natural hex architecture
I actually do not think your code should throw exceptions. It is really just an Either / result and then if something does blow its because you havent anticipated it via wrapping something in an either or result ... and so it should blow and the callers of your library should be submitting a defect..
This is a reasonable approach, but having a catch-all case that handles the exception and re-throws it, but wrapped in custom exception type can be problematic in case a new exception is added - the consumer of the API needs to go over their code and change the types everywhere, or else their catch code will not be working as before anymore.
This problem can easily go unnoticed, judging by my Haskell experience.
The solution would be to always use "checked exceptions" (or similar concept in your language, e.g. `ExceptT SomeEnumType` in Haskell) and never use catch-all cases (or wildcard patterns, in case of Haskell), so that every exception handling case is tagged with exception type, and type-checked.
This idea can also be explored in the Go programming language. Go has an error type, not exceptions, but error checking famously can be rather verbose.
Two cases to consider come to mind. First, the common pattern
Here the code is just passing along the error to the caller, unchanged.
Second, signaling errors ab initio
result := // some calculation or behavior
if result != expected
return fmt.Errorf("An error happened. Expected %v, go %v", expected, result)
In the first case, the discussions around whether or not to throw custom exceptions applies analogously: should you wrap the error or not?
The second case, I argue, is always wrong. The error is "stringly typed", and can be examined and read by a person, but that's it. The correct way is to define an error type meaningful for the context. Errors in Go are type implementing the error interface
type error interface {
Error() string
}
therefore, an error should be a type relevant or the context. A useful starting point looks something like
// some work
if result != expected {
return DomainError {
Status: AnErrorCode
Message: fmt.Sprintf("Error %v. Expected %v, go %v", AnErrorCode expected, result)
Details: []DomainType{ expected, result }
}
}
And the caller gets back a type providing useful information.
The whole DomainError thing only makes sense if you expect someone to handle this error (and by handle, I mean doing something other than logging and aborting some operation), or if you are writing a very generic library. Otherwise, it is wasted time and extra complexity that makes the code harder to read.
IME, returning a "stringly-typed" error is only wrong if you don't provide enough context in the returned error message. You can describe what you were doing as an error message and pass it back up the stack, each layer adding their own context, ie what they were trying to do to what. At the top you (should) get a pretty complete picture of what went wrong and why. I have found this reasonable and proportionate for most scenarios.
I agree with the first sentence. Your code should throw custom exceptions.
But it shouldn’t wrap other exceptions, if they are obvious. If library.readconfigfile(path) throws an IO exception while reading the file, just let it bubble up to the caller and don’t bother handling it. Just make sure your internal state is clean (catch..finally)
class Error(Exception):
pass
class FooError(Error):
…
and then, in the generic case:
try:
failing_external_function()
except Exception as e:
raise Error(*e.args) from e
(And if the code encounters a foo situation, it explicitly raises FooError, possibly with extra parameters, etc.)
It is really annoying to have to catch all of, say, socket.error, SSL errors, FileNotFound, etc. etc. in every call to some function in a module, if you use that module extensively. It’s much easier to just catch module.Error and be done with it. If you need to handle foo errors specially, you catch FooError.
What happens if function from another module calls failing_external_function()? Will you wrap it in another_module.Error?
This sounds like viral boilerplate: any function that can fail forces all other functions in the stack trace to be wrapped. Seems quite unpythonic, the whole point of exceptions is to avoid this boilerplate by propagating.
Are you just writing boilerplate to assign exceptions to modules? But this information is already present in the stack trace, what is the point? It's too generic to actually handle exceptions, and too redundant to provide debugging value.
There are some nice arguments for not overengineering in the comments here, as well as some for there still being merit in explicitly saying what could (typically) go wrong in the execution of a program. We've already seem the extreme of "never create your custom exceptions", or even something to the tune of "exception handling is a maintenance burden", but I can't help but to wonder about going in the exact opposite direction - even further than the article suggests.
What if we had a language that only had checked exceptions and forced you to deal with anything that can go wrong. Launching a program? You better have some code to deal with an out of memory exception, or some code for dealing with a stack overflow exception. Working with some maths? Well, you better define what should happen in the case of number underflows or overflows, as well as division by zero or whatever else can be inferred by what operators you use. Dealing with a network? Well, be prepared to handle dozens of types of network failures, the brittleness of networks being laid bare to you. Want to deal with reflection? Well, there would probably be none, to avoid too much dynamic behavior.
It's rather obvious why we don't really have languages like that, but at the same time - if writing code in a language like that would be "possible", then surely there's a domain or two out there that might benefit not just from enforced 100% test coverage, but also every single possible error being handled, or at least laid bare. If a program should crash upon particular errors, then the developer might say so explicitly, or otherwise provide logic to recover from those, without ever missing any place where things could go wrong.
Contrast this made up language with your typical Java project: you might use the Spring framework and have a method that exposes a RESTful API that returns some JSON to the client. You'd be amazed at just how many different issues you can run into with even the simplest implementations, it's like a never ending path of discovering more ways for your programs to go wrong. If you can sometimes benefit from your IDE going "hey, this code might throw a NullPointerException", then how much additional assistance you'd benefit from (and how much error handling should be encouraged/enforced) is probably up for debate!
> What if we had a language that only had checked exceptions and forced you to deal with anything that can go wrong. Launching a program? You better have some code to deal with an out of memory exception, or some code for dealing with a stack overflow exception. Working with some maths? Well, you better define what should happen in the case of number underflows or overflows, as well as division by zero or whatever else can be inferred by what operators you use. Dealing with a network? Well, be prepared to handle dozens of types of network failures, the brittleness of networks being laid bare to you. Want to deal with reflection? Well, there would probably be none, to avoid too much dynamic behavior.
> It's rather obvious why we don't really have languages like that, but at the same time - if writing code in a language like that would be "possible", then surely there's a domain or two out there that might benefit not just from enforced 100% test coverage, but also every single possible error being handled, or at least laid bare. If a program should crash upon particular errors, then the developer might say so explicitly, or otherwise provide logic to recover from those, without ever missing any place where things could go wrong.
A lot of functional languages go in that direction. E.g. if you stick to the non-IO fragment of Haskell then it mostly works like that - things that can error return Either that you have to handle explicitly, and while there's sugar to let you work with that in a similar way to exceptions it will never be entirely hidden. (Within IO you can have exceptions, of course; making a proper algebraic model of how e.g. network I/O works is pretty daunting). Idris or especially Noether go even further in that direction.
Like others have said, in theory this is great. In reality, I never see custom exceptions being handled differently than whatever exception was wrapped. And I have worked on some large distributed systems where failure is common. For new engineers these custom exceptions add abstract complexity and exception class hierarchy into a code base when it really isn’t needed.
I disagree. I have seen plenty of code that is forced to match on the text of an exception because it uses a too-generic type.
That said, I still wouldn't use a custom exception type in most languages simply because it's so tedious for a small pay-off. It's one of those things that you should do, but isn't really worth the hassle. Like putting alt text on HTML images.
Do any languages with exceptions let you define new exception types at the throw site?
[+] [-] hbrn|3 years ago|reply
1. You don't know which exceptions will be raised in advance. Anything that involves IO can fail in a plethora of ways, and you don't even know which calls involve IO (e.g. a library might choose to cache something on disk).
2. Consumers of your code will not know how to deal with those exceptions.
3. Most of exceptions are unrecoverable (that's why they are called exceptions), the best course of action is to crash, which happens by default.
4. You debug those exceptions by looking at stack trace. Adding extra levels just to give a fancy meaningless name to an exception does not help.
5. The whole point of exceptions is to propagate. Parthenon essentially suggests converting exceptions into return values.
[+] [-] zaphar|3 years ago|reply
1. Exceptions you expect your consumers to handle.
2. Exceptions you don't expect your consumers to handle.
The first one I would argue you should wrap third party exceptions. There is in nearly every case important context in your code that the thrower of the third party exception will not know and whoever is reading or handling the exception will want to know.
The second one should do whatever the equivalent of crashing is for your use case. Either exiting the program hard or bubbling to some top level handler where a "Something weird and unexpected happened and we can't continue whatever action was going on" alert or log get's recorded and the activity is terminated.
If something is throwing an exception and you don't know it can be throwing an exception it probably belongs in category 2. You may over time transition it to category 1 when you figure out that it is actually handleable.
In my experience though if you aren't disciplined then every exception ends up being lumped into category 2 whether it should be or not. Any language that helps you force the categorization gets bonus points from me.
[+] [-] eweise|3 years ago|reply
1. You don't know which exceptions will be raised in advance. Anything that involves IO can fail in a plethora of ways, and you don't even know which calls involve IO (e.g. a library might choose to cache something on disk).
Not sure how this relates to the code design decision
2. Consumers of your code will not know how to deal with those exceptions.
Not sure the point here but if the argument is that the caller won't know how to deal with the third party exception, I disagree. Typically it just needs to return a single exception type that wraps any underlying cause. Caller can just either decide to deal with it or rethrow.
3. Most of exceptions are unrecoverable (that's why they are called exceptions), the best course of action is to crash, which happens by default.
If the database is unresponsive do you want to crash your program? I wouldn't. I'll usually retry until its available.
4. You debug those exceptions by looking at stack trace. Adding extra levels just to give a fancy meaningless name to an exception does not help.
Really don't think an extra trace in the stack is a reason to not create a well defined contract.
5. The whole point of exceptions is to propagate. Parthenon essentially suggests converting exceptions into return values.
I didn't see anywhere in the doc where they mentioned converting exception into return values. Wrapped exceptions are still exceptions.
[+] [-] mcguire|3 years ago|reply
[+] [-] BrandoElFollito|3 years ago|reply
> Most of exceptions are unrecoverable (that's why they are called exceptions), the best course of action is to crash, which happens by default.
I prefer to propagate such unknown exceptions to a top-level catch to clanly log that something happened.
I usually have two types of exceptions: the ones I expect at some point (a HTTP call failing for some reason) that I may ("when I have time") group in "known exceptions we should not worry about" and log them as "informational", and exceptions I did not anticipate that I want to log as well, but with a critical level because they were, well, unexpected.
So crashing right when they happen may not be the best strategy.
[+] [-] WalterBright|3 years ago|reply
This is why D separates exceptions into two categories:
1. Error - these are not recoverable. The only reason to catch them is to maybe try to save some state or log a message or shut down the reactor before crashing.
2. Exception - these are recoverable
(I'm being facetious. Any system design where, while unwinding a fatal error, one relies on it to shut down the reactor is a horrible, terrible design.)
[+] [-] drewcoo|3 years ago|reply
I thought the point of exceptions was what we're now calling observability.
If somehow wrapping them in your own goo because you, the dev of that code, can help diagnose problems, more power to you.
But most of the time that doesn't really help so letting them bubble up and not obscuring root causes is better.
So . . . Use judgement. If you don't have judgement, ask a friend. If you don't have a friend, just let the exceptions propagate on their own.
[+] [-] charcircuit|3 years ago|reply
[+] [-] P5fRxh5kUvp2th|3 years ago|reply
I'm going to curse here, but ... fucking seriously.
If I get a .net ADO exception coming out of a 3rd party library it's absolutely not going to shock me or throw me for a loop. But do you know what IS a pain in the ass? Using 3 different libraries, all that wrap that same ADO exception in their own custom exception.
[+] [-] eweise|3 years ago|reply
Yes you should wrap ALL third party exceptions for the reasons given in the post. There is no 'just in case' reason. Any third party exception returned may cause your client to be dependent on it.
[+] [-] julik|3 years ago|reply
This is checked exceptions all over again... Frankly, I usually do exactly the opposite. Most cases I've seen the exceptions that can arise are not widely known in advance, and for most spots where exceptions can be raised from I simply do not care about them as much either. If my DB is not accessible - do I really, really need to wrap it? If the DNS resolver is failing - do I really really need to wrap it?
When debugging I actually appreciate when a library/controller/module/whatnot does not attempt to "enhance" the exception from something outside of its control with a wrapper, and value seeing the original failure instead.
[+] [-] gadflyinyoureye|3 years ago|reply
I would say yes. Most of your code doesn't care that the database failed with a PG-300292-AB error. What you do care about is that there is a system failure. If you wrap your system failure, and document it as THE exception, the API caller will know what to look for.
In general there really are only a few exceptions: system, invalid input, non found. I'm probably missing one or two others. Most exceptions/errors are one of these. If you make these 3-5 exceptions/errors explicit, you're APIs will be pretty clear. A function can pretty much always returns a system exception (bad IO, sunspots, etc.). It can also throw an invalid input exception. Your client can handle these cases differently. For a HTTP RPC service, you return a 500 for system and 400 or 422 in the example. Not bad.
One can even wrap the exception with a more detailed message (which I know you said you didn't like) that preserves flow. So you get a invalid input exception, you can add details around the null value by properly nesting the messages. They should show up in your logs.
[+] [-] rileymat2|3 years ago|reply
That is an interesting topic, as I went through the cycle of adding and removing them, they always felt like the right thing, but they felt like "work". They make coding less fun, having to think through failure conditions, like you say, you don't care about.
I guess the answer is really there is no one sized fits all solution, we have some applications where the correct solution is to crash, we have other applications that can't crash under any condition. You just need to know where you are.
Edit:
Also, out of the box C++ checked exceptions implemented terribly.
http://www.gotw.ca/publications/mill22.htm
The (Mis)understanding section describes it pretty well.
[+] [-] teddyh|3 years ago|reply
This is what Python’s “raise from” is for; to preserve the original exception.
[+] [-] Sakos|3 years ago|reply
[+] [-] aleksiy123|3 years ago|reply
If you want to reduce coupling you should avoid throwing custom exceptions at all. Semantic information can go in the error message and log. The error type should be used to indicate to your program whether an error is recoverable, retriable or some other action needs to be taken. For example google on has 16 canonical error codes for all APIs.
https://github.com/googleapis/googleapis/blob/master/google/...
[+] [-] CharlieDigital|3 years ago|reply
What was null? Why might it be null? Mis-configuration? Missing configuration? Can't load some data or connect to some system? Which setting should be verified? Give simple hints on what might have gone wrong to help future you.
[+] [-] BurningFrog|3 years ago|reply
In that mindset, any non zero benefit to X means you must do X.
[+] [-] MrTortoise|3 years ago|reply
but this is far nicer way of saying the same thing https://ericlippert.com/2008/09/10/vexing-exceptions/
I actually do not think your code should throw exceptions. It is really just an Either / result and then if something does blow its because you havent anticipated it via wrapping something in an either or result ... and so it should blow and the callers of your library should be submitting a defect..
[+] [-] klntsky|3 years ago|reply
This problem can easily go unnoticed, judging by my Haskell experience.
The solution would be to always use "checked exceptions" (or similar concept in your language, e.g. `ExceptT SomeEnumType` in Haskell) and never use catch-all cases (or wildcard patterns, in case of Haskell), so that every exception handling case is tagged with exception type, and type-checked.
[+] [-] Traubenfuchs|3 years ago|reply
You can at any point throw ResponseStatusException + http status code + optional message, for example
You can also define an error handler for this exception type to convert it in whatever error response format you desire.Writing custom exceptions for everything is the definition of overengineering, falls into the YAGNI trap and means kicking KISS with your feet.
[+] [-] unknown|3 years ago|reply
[deleted]
[+] [-] cratermoon|3 years ago|reply
Two cases to consider come to mind. First, the common pattern
Here the code is just passing along the error to the caller, unchanged.Second, signaling errors ab initio
In the first case, the discussions around whether or not to throw custom exceptions applies analogously: should you wrap the error or not?The second case, I argue, is always wrong. The error is "stringly typed", and can be examined and read by a person, but that's it. The correct way is to define an error type meaningful for the context. Errors in Go are type implementing the error interface
therefore, an error should be a type relevant or the context. A useful starting point looks something like then code can look like And the caller gets back a type providing useful information.[+] [-] tsimionescu|3 years ago|reply
[+] [-] kitd|3 years ago|reply
[+] [-] andix|3 years ago|reply
But it shouldn’t wrap other exceptions, if they are obvious. If library.readconfigfile(path) throws an IO exception while reading the file, just let it bubble up to the caller and don’t bother handling it. Just make sure your internal state is clean (catch..finally)
[+] [-] teddyh|3 years ago|reply
It is really annoying to have to catch all of, say, socket.error, SSL errors, FileNotFound, etc. etc. in every call to some function in a module, if you use that module extensively. It’s much easier to just catch module.Error and be done with it. If you need to handle foo errors specially, you catch FooError.
[+] [-] hbrn|3 years ago|reply
This sounds like viral boilerplate: any function that can fail forces all other functions in the stack trace to be wrapped. Seems quite unpythonic, the whole point of exceptions is to avoid this boilerplate by propagating.
Are you just writing boilerplate to assign exceptions to modules? But this information is already present in the stack trace, what is the point? It's too generic to actually handle exceptions, and too redundant to provide debugging value.
[+] [-] KronisLV|3 years ago|reply
What if we had a language that only had checked exceptions and forced you to deal with anything that can go wrong. Launching a program? You better have some code to deal with an out of memory exception, or some code for dealing with a stack overflow exception. Working with some maths? Well, you better define what should happen in the case of number underflows or overflows, as well as division by zero or whatever else can be inferred by what operators you use. Dealing with a network? Well, be prepared to handle dozens of types of network failures, the brittleness of networks being laid bare to you. Want to deal with reflection? Well, there would probably be none, to avoid too much dynamic behavior.
It's rather obvious why we don't really have languages like that, but at the same time - if writing code in a language like that would be "possible", then surely there's a domain or two out there that might benefit not just from enforced 100% test coverage, but also every single possible error being handled, or at least laid bare. If a program should crash upon particular errors, then the developer might say so explicitly, or otherwise provide logic to recover from those, without ever missing any place where things could go wrong.
Contrast this made up language with your typical Java project: you might use the Spring framework and have a method that exposes a RESTful API that returns some JSON to the client. You'd be amazed at just how many different issues you can run into with even the simplest implementations, it's like a never ending path of discovering more ways for your programs to go wrong. If you can sometimes benefit from your IDE going "hey, this code might throw a NullPointerException", then how much additional assistance you'd benefit from (and how much error handling should be encouraged/enforced) is probably up for debate!
[+] [-] lmm|3 years ago|reply
> It's rather obvious why we don't really have languages like that, but at the same time - if writing code in a language like that would be "possible", then surely there's a domain or two out there that might benefit not just from enforced 100% test coverage, but also every single possible error being handled, or at least laid bare. If a program should crash upon particular errors, then the developer might say so explicitly, or otherwise provide logic to recover from those, without ever missing any place where things could go wrong.
A lot of functional languages go in that direction. E.g. if you stick to the non-IO fragment of Haskell then it mostly works like that - things that can error return Either that you have to handle explicitly, and while there's sugar to let you work with that in a similar way to exceptions it will never be entirely hidden. (Within IO you can have exceptions, of course; making a proper algebraic model of how e.g. network I/O works is pretty daunting). Idris or especially Noether go even further in that direction.
[+] [-] greatpostman|3 years ago|reply
[+] [-] IshKebab|3 years ago|reply
That said, I still wouldn't use a custom exception type in most languages simply because it's so tedious for a small pay-off. It's one of those things that you should do, but isn't really worth the hassle. Like putting alt text on HTML images.
Do any languages with exceptions let you define new exception types at the throw site?
[+] [-] vbezhenar|3 years ago|reply
For libraries that's more nuanced and good unchecked exception hierarchy is a part of good API.
[+] [-] DotaFan|3 years ago|reply
[+] [-] unknown|3 years ago|reply
[deleted]
[+] [-] xwowsersx|3 years ago|reply