www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Can we make Throwable an interface?

reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
Then we could use interfaces as "tags" for exceptions and catch using 
one of many interfaces an exception has. Consider DIP33:
http://wiki.dlang.org/DIP33

The 2 problems it had were:

1. enums are hard to extend for std lib, and absolutely impossible by 
3rd party libraries.
2. Single hierarchy has some appeal but it doesn't allow to catch on 
similar classes that do not inherit from the same base class. Basically 
there are many ways to view similarities of excpetions and single 
hierarchy fails to address that.

If we were to replace each class with a base interface and every Kind 
enum with an interface (inhereting from one or more base interfaces) 
then we can actually address both of these points.

To druntime experts - is it hard to change Throwable to be an interface? 
What exactly EH system needs of Throwable?

-- 
Dmitry Olshansky
Dec 09 2014
next sibling parent reply "Sean Kelly" <sean invisibleduck.org> writes:
All the stack tracing stuff is wired through Throwable, so some
duplication may need to occur if it were changed to an interface.
Dec 09 2014
next sibling parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
09-Dec-2014 21:00, Sean Kelly пишет:
 All the stack tracing stuff is wired through Throwable, so some
 duplication may need to occur if it were changed to an interface.
Do yYou mean that interface can't contain the code for tracing so we'd have to duplicate it in Error and/or Exception? But interfaces can contain final methods just fine and tracing seems like something not overridable nor public, how the whole problem area looks like? -- Dmitry Olshansky
Dec 09 2014
prev sibling parent "Lars T. Kyllingstad" <public kyllingen.net> writes:
On Tuesday, 9 December 2014 at 18:00:05 UTC, Sean Kelly wrote:
 All the stack tracing stuff is wired through Throwable, so some
 duplication may need to occur if it were changed to an 
 interface.
I can think of two ways to solve this: 1. rename the current Throwable (to e.g. ThrowableBase) and make it inherit the new Throwable interface. 2. Move the relevant functionality to a mixin template that is used in a limited set of base exception classes. Lars
Dec 10 2014
prev sibling next sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Tue, Dec 09, 2014 at 08:06:32PM +0300, Dmitry Olshansky via Digitalmars-d
wrote:
 Then we could use interfaces as "tags" for exceptions and catch using one of
 many interfaces an exception has. Consider DIP33:
 http://wiki.dlang.org/DIP33
 
 The 2 problems it had were:
 
 1. enums are hard to extend for std lib, and absolutely impossible by
 3rd party libraries.
 2. Single hierarchy has some appeal but it doesn't allow to catch on
 similar classes that do not inherit from the same base class.
 Basically there are many ways to view similarities of excpetions and
 single hierarchy fails to address that.
 
 If we were to replace each class with a base interface and every Kind
 enum with an interface (inhereting from one or more base interfaces)
 then we can actually address both of these points.
[...] I like this idea! It would also address the current messy situation with OS-specific exceptions (e.g., ErrnoException or WindowsException, or whatever it's called) vs. semantically-oriented logical exceptions (FileNotFoundException, NoAccessException, etc.). OS-specific exceptions are necessary to capture OS-specific information, but user code generally wants to catch according to more semantic / logical criteria. For example, dirEntries may fail due to permission failure, but the user is generally not interested in OS-specific error codes like errno or Windows error numbers; what is more interesting is "was this failure caused by permission error?". This problem cannot be satisfactorily resolved with a single Exception hierarchy, but it *can* be resolved by using interfaces instead of base classes. We could then have a WindowsException and a PosixErrnoException (for example), and have subclasses also implement a NoAccessException interface. Thus you have a class hierarchy based on implementation details (e.g., PosixErrorException stores errno, and WindowsException stores Windows error codes), but also an interface hierarchy based on logical categorizations of exceptions. T -- If I were two-faced, would I be wearing this one? -- Abraham Lincoln
Dec 09 2014
next sibling parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
09-Dec-2014 21:05, H. S. Teoh via Digitalmars-d пишет:

 1. enums are hard to extend for std lib, and absolutely impossible by
 3rd party libraries.
 2. Single hierarchy has some appeal but it doesn't allow to catch on
 similar classes that do not inherit from the same base class.
 Basically there are many ways to view similarities of excpetions and
 single hierarchy fails to address that.

 If we were to replace each class with a base interface and every Kind
 enum with an interface (inhereting from one or more base interfaces)
 then we can actually address both of these points.
[...] I like this idea!
[snip]
 For example, dirEntries may fail due to permission failure, but the user
 is generally not interested in OS-specific error codes like errno or
 Windows error numbers; what is more interesting is "was this failure
 caused by permission error?".

 This problem cannot be satisfactorily resolved with a single Exception
 hierarchy, but it *can* be resolved by using interfaces instead of base
 classes. We could then have a WindowsException and a PosixErrnoException
 (for example), and have subclasses also implement a NoAccessException
 interface. Thus you have a class hierarchy based on implementation
 details (e.g., PosixErrorException stores errno, and WindowsException
 stores Windows error codes), but also an interface hierarchy based on
 logical categorizations of exceptions.
That's exactly what I aim to do. The question is how hard it's do in runtime and if there are any critical assumptions that prevent this. -- Dmitry Olshansky
Dec 09 2014
prev sibling parent reply "Kagamin" <spam here.lot> writes:
On Tuesday, 9 December 2014 at 18:07:16 UTC, H. S. Teoh via 
Digitalmars-d wrote:
 what is more interesting is "was this failure caused by 
 permission error?".
And what if it does? You would create thousands of types and their cartesian products just to check for one of them?
Dec 10 2014
parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
10-Dec-2014 11:20, Kagamin пишет:
 On Tuesday, 9 December 2014 at 18:07:16 UTC, H. S. Teoh via
 Digitalmars-d wrote:
 what is more interesting is "was this failure caused by permission
 error?".
And what if it does? You would create thousands of types and their cartesian products just to check for one of them?
There is no need for cartesian products. E.g. interface ConversionException { } interface Overflow : ConversionException {} interface Underflow : ConversionException {} ... Basically enum in DIP 33 expends to a set of dervied interfaces. Anyhow enums do not allow one to catch based on them which already makes them highly unusable. -- Dmitry Olshansky
Dec 10 2014
parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Wed, Dec 10, 2014 at 10:47:46PM +0300, Dmitry Olshansky via Digitalmars-d
wrote:
 10-Dec-2014 11:20, Kagamin пишет:
On Tuesday, 9 December 2014 at 18:07:16 UTC, H. S. Teoh via
Digitalmars-d wrote:
what is more interesting is "was this failure caused by permission
error?".
And what if it does? You would create thousands of types and their cartesian products just to check for one of them?
There is no need for cartesian products. E.g. interface ConversionException { } interface Overflow : ConversionException {} interface Underflow : ConversionException {} ... Basically enum in DIP 33 expends to a set of dervied interfaces. Anyhow enums do not allow one to catch based on them which already makes them highly unusable.
[...] Exactly, that's what makes interfaces a good solution to this. Enums are also non-extensible, since once Phobos fixes the enum values, user code cannot extend it, even if a user-defined Exception naturally falls under one of the preexisting exception categories. Using interfaces would allow users to leverage the existing exception hierarchy instead of reinventing the square wheel every time. T -- This is not a sentence.
Dec 10 2014
prev sibling next sibling parent reply "Kagamin" <spam here.lot> writes:
On Tuesday, 9 December 2014 at 17:06:45 UTC, Dmitry Olshansky 
wrote:
 1. enums are hard to extend for std lib, and absolutely 
 impossible by 3rd party libraries.
What's the problem? When you add new functionality to std lib, you add an enum entry in the same pull request. 3rd party libraries define their specific exceptions and enums.
Dec 10 2014
parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
10-Dec-2014 11:24, Kagamin пишет:
 On Tuesday, 9 December 2014 at 17:06:45 UTC, Dmitry Olshansky wrote:
 1. enums are hard to extend for std lib, and absolutely impossible by
 3rd party libraries.
What's the problem?
That you miss the point of standard exception hierarchy - that is client code shall not be concerned with 3-rd party exceptions of every library as long as 3rd parties use common hierarchy(s). A big problem is we can't catch based on enum or would have to rethrow. Another problem is that one hierarchy is too rigid and exceptions can be viewed from different angles (implementation-wise like WindowsException and semantic categories like PermissionException).
 When you add new functionality to std lib, you add
 an enum entry in the same pull request.
Aye. And every program that had final switch on it fails. There is too many opportunities to break code with that. In contrast with interfaces one explicitly has open set of possible derived interfaces.
 3rd party libraries define their
 specific exceptions and enums.
Which is utter failure. If every MyFooBar library defines MyFooBar exception then the whole point of standard exceptions is moot. We degrade back to specific per library error codes, with a benefit that they are transported across stack frames. The point is to _catch_ based on common failure causes the same way across _any_ libraries. The 3rd parties are then free to provide more specific causes of say OsException or FormatException etc. which may then find their way into standard. So that all arithmetic libraries inherit Underflow and Overflow as needed in their custom exceptions, or even just use anonymous class and be done. This allows extension while keeping client's code simple. Unlike DIP33 where one cannot make FormatException grow custom enum flag for his own library _only_ thus requiring client code to _always_ deal with your custom exception explicitly. -- Dmitry Olshansky
Dec 10 2014
prev sibling parent reply "Lars T. Kyllingstad" <public kyllingen.net> writes:
On Tuesday, 9 December 2014 at 17:06:45 UTC, Dmitry Olshansky 
wrote:
 Then we could use interfaces as "tags" for exceptions and catch 
 using one of many interfaces an exception has.
I think that is an excellent idea! I have looked a bit at boost::exception, and wanted for a while to incorporate something similar into DIP33. It has two important features: Firstly, it allows semantic tagging of exceptions, which is implemented in C++ by means of multiple (virtual) inheritance. Your suggestion of using interfaces for this would fit the bill nicely, I think. For interfaces that have extra functions, we could supply standard implementations of those functions as mixin templates for when users don't want to implement those functions themselves. Secondly, boost::exception allows for transport of arbitrary data to the catch site, and importantly, supplying additional data as the exception propagates up the call chain. (E.g., the lowest-level I/O function only has a file descriptor and can only provide an errno code, so the file name must be supplied at a higher level, e.g. in std.stdio.File. Furthermore, the file was opened in some context, e.g. "read image file", which may also be useful to know at the catch site.) I'm not sure what is the best way to achieve this in D, but one option is to use an (associative?) array of Variants.
 Consider DIP33:
 http://wiki.dlang.org/DIP33

 The 2 problems it had were:

 1. enums are hard to extend for std lib, and absolutely 
 impossible by 3rd party libraries.
 2. Single hierarchy has some appeal but it doesn't allow to 
 catch on similar classes that do not inherit from the same base 
 class. Basically there are many ways to view similarities of 
 excpetions and single hierarchy fails to address that.
I consider (2) the biggest problem by far. The enum solution doesn't preclude extension by subclassing – in fact, that was the main purpose of the "unknown" constant in the "Kind" enums. Lars
Dec 10 2014
parent reply Jacob Carlborg <doob me.com> writes:
On 2014-12-11 08:21, Lars T. Kyllingstad wrote:

 I'm not sure what is the best way to achieve this in D, but one option is to
use
 an (associative?) array of Variants.
How does Boost implement this? -- /Jacob Carlborg
Dec 11 2014
parent "Lars T. Kyllingstad" <public kyllingen.net> writes:
On Thursday, 11 December 2014 at 15:04:53 UTC, Jacob Carlborg
wrote:
 On 2014-12-11 08:21, Lars T. Kyllingstad wrote:

 I'm not sure what is the best way to achieve this in D, but 
 one option is to use
 an (associative?) array of Variants.
How does Boost implement this?
Classes that contain the additional information must inherit boost::error_info, and each exception contains a map<type_info, shared_ptr<error_info>>. We could do more or less the exact same thing in D, of course, with ErrorInfo[TypeInfo], or even Object[TypeInfo], but I think I'd prefer a non-polymorphic solution.
Dec 11 2014