ExceptionVariance
From APIDesign
(One intermediate revision not shown.) | |||
Line 41: | Line 41: | ||
==== Usage in [[Truffle]] ==== | ==== Usage in [[Truffle]] ==== | ||
- | This is the approach [[I]] am now trying to follow during my work on the [[Truffle]] [[API]]: clients using the system and | + | This is the approach [[I]] am now trying to follow during my work on the [[Truffle]] [[API]]: clients using the system and evaluating their code snippets don't need to care about any [[checked exception]]s. On the other hand people who implement [[Truffle]] languages (e.g. [[ProviderAPI]]) can throw any exception and it is the responsibility of the system (e.g. the [[Truffle]] [[API]]) to convert the (for example) parsing exception into something clients can swallow. Right now we are ''unchecking any [[checked exception]]'' so it can easily propagate, but other more intrinsic ways of handling them communication between [[ProviderAPI]] and [[ClientAPI]] (while keeping their [[cluelessness]]) are certainly possible. |
[[Category:APIDesignPatterns]] | [[Category:APIDesignPatterns]] | ||
- | [[Category:APIDesignPatterns: | + | [[Category:APIDesignPatterns:Exceptions]] |
Current revision
This essay is most useful when dealing with Checked exceptions as known from Java, however even in systems without compile time exception checking it may be useful. The same principle of Covariance/Contravariance of Exceptions applies in any language.
When creating both sides of an API - e.g. its ClientAPI facade as well as its ProviderAPI part, it is interesting to note that the way one deals with exceptions is (or at least should be) different.
Contents |
The ClientAPI Point of View
The biggest drawback of Checked exceptions in Java is when they force callers to catch them and there is no reasonable recovery. How we can prevent that? Well, the easiest thing is to not throw any checked exceptions to the client. Then the clients don't need to care about the exceptions at compile time. Btw. I have to mention that I believe there are situations when clients should care about exceptions and then use the checked exceptions seems OK. But, if we want to take things to the extreme and if we want to make life of our [[clueless] API users absolutely exception-checkless: the less of compile time checked exceptions we throw - the easier for them during compilation.
The ProviderAPI Point of View
However if we are creating a ProviderAPI - e.g. some interface others have to implement - the situation is quite different. We may ask them sum two numbers, but who knows what they need to do to compute that!? Maybe then need to read some files (and that in Java automatically yields IOException checked exception) or connect to network (also requires users to deal with their checked exceptions). If we don't allow them to propagate the exceptions, what can they do? They have to catch them, recover from them or re-throw them. That is often correct, but certainly not clueless. What can we do to increase cluelessness of people who implement methods in a ProviderAPI that we created? Yes, we should declare that these methods can throw any Exception.
Don't Mix ClientAPI and ProviderAPI
This is just another example of APIvsSPI difference! These needs and expectations for dealing with exceptions when using a ClientAPI are different then when implementing a ProviderAPI. Obvious conclusion: never mix ClientAPI and ProviderAPI as then you cannot satisfy the different needs.
What shall we do (to increase cluelessness of everyone)? We have to adopt the APIvsSPI separation - e.g. have a special types for ClientAPI, have another special types for ProviderAPI and let our API internals to be a mediator between these two. Let's try to demonstrate the approach on a decryption example. The client may have an array of bytes and wants to ask the system to decode them:
byte[] arr = ...; byte[] output = new byte[arr.length]; if (Decoder.decode(arr, output)) { // doesn't throw any checked exceptions System.out.println("The result is " + output); } else { System.err.println("Cannot decode"); }
in this example the client doesn't have to deal with any exceptions. The only problem that can appear is that the decoding isn't allowed, but that one is signaled by a false return value (not that I would consider such return value as a best alternative, but it shows the point). Now imagine a ProviderAPI for those who want to write their own decoder:
public interface DecoderImplementation { public void decode(byte[] input, byte[] output) throws Exception; // yes, let's allow any error } public final class CannotDecodeException extends Exception { }
now: whoever provides the decoder implementation, isn't limited by any exceptions. It is possible to throw anything - yet, some exceptions make bigger sense than other. For example the CannotDecodeException is well understood by the system and gets converted into false return value from the ClientAPI call to decode something. The other exceptions then just propagate. How can that be done? It can be done because the ClientAPI doesn't deal directly with DecoderImplementations, but rather thru a mediator - the API. And the API makes sure it converts the well understood ProviderAPI exceptions into the output ClientAPI can understand too, while allowing both sides to be almost completely clueless about the checked exceptions.
Usage in Truffle
This is the approach I am now trying to follow during my work on the Truffle API: clients using the system and evaluating their code snippets don't need to care about any checked exceptions. On the other hand people who implement Truffle languages (e.g. ProviderAPI) can throw any exception and it is the responsibility of the system (e.g. the Truffle API) to convert the (for example) parsing exception into something clients can swallow. Right now we are unchecking any checked exception so it can easily propagate, but other more intrinsic ways of handling them communication between ProviderAPI and ClientAPI (while keeping their cluelessness) are certainly possible.