June 27, 2017 Re: Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Tobias Müller | On Tuesday, 27 June 2017 at 06:10:52 UTC, Tobias Müller wrote: > Moritz Maxeiner <moritz@ucworks.org> wrote: >> [...] >> Or, more succinct: You must either manually write things down the >> compiler could find out in a fraction of the time via static >> analysis, or cheat the system; both cases are bad code. > > It's not at all bad code to write things down that the compiler could > infer, quite the opposite. Of course it is bad, because the compiler can do it better (no chance for a wrong exception set sans compiler bugs) and faster than you. > Writing it down signals _intent_ and the compiler can check if the implementation is matching the specification Verifying that a function meets its specification is what unittests are for (asserts for runtime behaviour, static asserts for types). With a function set trait there's nothing stopping you from writing a template that allows you to do the following: --- void foo() { ... } unittest { static assert (throws!(foo, AException)); static assert (throwsAll!(foo, AException, BException)); static assert (throwsAny!(foo, AException, CException)); static assert (!throws!(foo, CException)); } --- which would be idiomatic D, giving you not only the same guarantees as checked exceptions (without the downsides), but also even more versatility by allowing arbitrary checks on the exception set. > which gives you additional security. This has nothing to do with security; you may be thinking of safety. > Additionally it allowes the compiler to do the checks locally which is much > easier. Easier to implement in the compiler, yes. That's precisely why I called it a compiler deficiency compared to exposing the function exception set, which is more advanced. > Function signatures are interfaces which should be self-contained IMO, i.e. it should not be necessary to examine the function body. > That's what signatures are for. At very least for public interfaces. Sure, but since a function's signature in D does not include its exception set (be they checked or not), that point is moot. > I honestly don't understand how people that care a great deal about expressive type systems can be so opposed to checked exceptions. After all they wouldn't use 'object' for everything either. For exactly the reasons I have already explained. |
June 27, 2017 Re: Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to mckoder | I think it's important to understand, D is *not* Java. |
June 27, 2017 Re: Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Moritz Maxeiner | On Tuesday, 27 June 2017 at 11:40:02 UTC, Moritz Maxeiner wrote:
>>
>> It's not at all bad code to write things down that the compiler could infer, quite the opposite.
>
> Of course it is bad, because the compiler can do it better (no chance for a wrong exception set sans compiler bugs) and faster than you.
>
>> Writing it down signals _intent_ and the compiler can check if the implementation is matching the specification
>
> Verifying that a function meets its specification is what unittests are for (asserts for runtime behaviour, static asserts for types).
You might as well argue that you shouldn't have to declare the return type of functions because the compiler can determine that automatically based on the types of values you are actually returning. Then write unit tests to make sure that the return type as determined by the compiler matches what you intended.
|
June 27, 2017 Re: Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to mckoder | On Tuesday, 27 June 2017 at 10:18:04 UTC, mckoder wrote: > "I think that the belief that everything needs strong static (compile-time) checking is an illusion; it seems like it will buy you more than it actually does. But this is a hard thing to see if you are coming from a statically-typed language." > > Source: https://groups.google.com/forum/#!original/comp.lang.java.advocacy/r8VPk4deYDI/qqhL8g1uvf8J > > If you like dynamic languages such as Python you probably agree with Bruce Eckel. If you are a fan of static checking then I don't see how you can like that. A quote from Uncle Bob about too much static typing and checked exceptions: http://blog.cleancoder.com/uncle-bob/2017/01/11/TheDarkPath.html "My problem is that [Kotlin and Swift] have doubled down on strong static typing. Both seem to be intent on closing every single type hole in their parent languages." "I would not call Java a strongly opinionated language when it comes to static typing. You can create structures in Java that follow the type rules nicely; but you can also violate many of the type rules whenever you want or need to. The language complains a bit when you do; and throws up a few roadblocks; but not so many as to be obstructionist. Swift and Kotlin, on the other hand, are completely inflexible when it comes to their type rules. For example, in Swift, if you declare a function to throw an exception, then by God every call to that function, all the way up the stack, must be adorned with a do-try block, or a try!, or a try?. There is no way, in this language, to silently throw an exception all the way to the top level; without paving a super-hiway for it up through the entire calling tree." |
June 27, 2017 Re: Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to mckoder | On Tuesday, 27 June 2017 at 16:01:37 UTC, mckoder wrote: > On Tuesday, 27 June 2017 at 11:40:02 UTC, Moritz Maxeiner wrote: >>> >>> It's not at all bad code to write things down that the compiler could infer, quite the opposite. >> >> Of course it is bad, because the compiler can do it better (no chance for a wrong exception set sans compiler bugs) and faster than you. >> >>> Writing it down signals _intent_ and the compiler can check if the implementation is matching the specification >> >> Verifying that a function meets its specification is what unittests are for (asserts for runtime behaviour, static asserts for types). > > You might as well argue that you shouldn't have to declare the return type of functions because the compiler can determine that automatically based on the types of values you are actually returning. Of *course* you shouldn't have to (-> auto functions [1]). The difference being, of course, that specifying the return type is always only a single type, whereas specifying the exception set blows up in verbosity with each additional level of call hierarchy. > Then write unit tests to make sure that the return type as determined by the compiler matches what you intended. If you don't trust the compiler's return type inference you can do that, of course. Though in contrast to the exception set there's a lot less reason to do so in the return type case, because it's only a single type not an arbitrarily large set of types. [1] https://dlang.org/spec/function.html#auto-functions |
June 27, 2017 Re: Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Moritz Maxeiner | As Tobias mentioned, there are safety implications to "auto" behavior. In the example below I am using C# and its "var" feature: class A { public static Employee getFoo() { return getPoorPerformingEmployee(); } } This is your code, in which you are calling A which was written by someone else: class B { var foo = A.getFoo(); foo.fire(); } Now another programmer changes class A as follows: class A { public static Missile getFoo() { return new Missile(); } } Guess what happens now? You intended to fire an employee, but instead you have fired a missile. The compiler did not catch this grave mistake because you did not clearly state your intent. Your class B compiles fine because both Employee and Missile have a method named fire(). You could have expressed your intent more clearly like this: class B { Employee foo = A.getFoo(); foo.fire(); } Now the compiler is able to catch your mistake. This is the reason your code becomes safer when you express your intent clearly. You should have the option to state your intent clearly by listing the exceptions that can be thrown by a method. In dynamic languages like Python there is less ability to state intent clearly. As a result in such languages fewer bugs can be caught at compile time. More bugs show up at run time. This may be acceptable if the program is intended for internal use in a company, because when the program crashes they can call you to come and fix it because you are in the next office. But when reliability is important then the more opportunity there is to express intent and have the compiler verify your code against your intent, the better. |
June 27, 2017 Re: Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to jag | On Tuesday, 27 June 2017 at 18:14:47 UTC, jag wrote: > As Tobias mentioned, there are safety implications to "auto" behavior. In the example below I am using C# and its "var" feature: > > [...] Your example uses variable type inference, which happens on the *caller* side, *not* the *callee* side, the latter of which being where both checked exceptions and return type inference happen. The correct analogue to your example in the exception domain is thus *not* checked exceptions, but verifying the exception set *at the call site* (the same way you fix the variable type *at the call site* to `Employee`), which is exactly what you can do by exposing the function exception set via a trait. > Now the compiler is able to catch your mistake. This is the reason your code becomes safer when you express your intent clearly. You should have the option to state your intent clearly by listing the exceptions that can be thrown by a method. As I have pointed out, your example occurs on the *caller* side, not the *callee* side. The proper solution is not for the callee to specify which exceptions it may throw, but for the caller to specify which exceptions it allows the callee to throw (using compile time introspection on the exception set). > In dynamic languages like Python [...] None of this justifies putting the burden on the callee side (i.e. checked exceptions), instead of on the caller side (the latter being implementable in an idiomatic way using a trait as John has proposed). |
June 27, 2017 Re: Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Moritz Maxeiner | On Tuesday, 27 June 2017 at 19:37:24 UTC, Moritz Maxeiner wrote:
>
> As I have pointed out, your example occurs on the *caller* side, not the *callee* side. The proper solution is not for the callee to specify which exceptions it may throw, but for the caller to specify which exceptions it allows the callee to throw (using compile time introspection on the exception set).
Can I as a programmer who wants to call a function written by someone else inspect the declaration of that function and know what exceptions are possible? If no how is this supposed to work? I am supposed to specify that exceptions I want to allow the called function to throw? The called function is not going to dynamically adapt itself and change the list of exceptions it throws, right? So how can I know, before running the code, what exceptions are possible?
|
June 27, 2017 Re: Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Tobias Müller | On Tuesday, 27 June 2017 at 06:10:52 UTC, Tobias Müller wrote:
> I honestly don't understand how people that care a great deal about expressive type systems can be so opposed to checked exceptions. After all they wouldn't use 'object' for everything either.
>
> Tobi
I think there is a threshold. For example, personally I am against the compiler refusing to compile because of an unused variable, or import clause. Why, because they get in the way of making changes. I don't want to pacify the compiler as I progress through the development process. Generally when the compiler complains about a type mismatch I actually forgot to make additional changes to make the new thing work.
Checked exceptions do the same thing. Call a new function, propagate the exception just to determine if it is a desired change. This very much can lead to attempting to pacify without properly handling.
This discussion seems to emphasize the ability to handle every exception type. There are certainly times I've utilized the ability to handle different types of exception in different ways, but it isn't the norm. This is likely because generally I find Exception to crop up because of programming logic bugs rather than true exceptions and in the other cases I'm not using the type of exception to change how I deal with a problem; if my Json parsing throws a Conversion exception I'm not going to handle it differently than when it throws an Invalid Token exception.
I however don't have much experience managing checked Exceptions. I remember back in the day trying to fix the long list of exceptions my functions could throw. I'd commonly use throws Exception, pondered on wrapping exceptions into my own exception that would then be thrown. Neither of these would be helpful to the original points, adding exceptions should break the contract and you should be able to catch specific exceptions. How is the ever expanding exception list of static void Main managed by others?
|
June 27, 2017 Re: Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to jag | On Tuesday, 27 June 2017 at 21:47:49 UTC, jag wrote: > On Tuesday, 27 June 2017 at 19:37:24 UTC, Moritz Maxeiner wrote: >> >> As I have pointed out, your example occurs on the *caller* side, not the *callee* side. The proper solution is not for the callee to specify which exceptions it may throw, but for the caller to specify which exceptions it allows the callee to throw (using compile time introspection on the exception set). > > Can I as a programmer who wants to call a function written by someone else inspect the declaration of that function and know what exceptions are possible? * Can I as a programmer who wants to call a function written by someone else inspect that function's exception set? Yes, as explained fully here [1], shown as an idiomatic D trait here [2], and a template based abstraction around that trait here [3]. It would need to be implemented, of course, but it's not conceptually hard. > If no how is this supposed to work? It was explained multiple times in this thread. > I am supposed to specify that exceptions I want to allow the called function to throw? You aren't supposed to do anything, but you can do that, yes. > The called function is not going to dynamically adapt itself and change the list of exceptions it throws, right? What? A function's exception set is determined by its body and can be aggregated by the compiler in a single recursive pass in the static analysis phase. > So how can I know, before running the code, what exceptions are possible? You mean the very first time you want to call it and you don't know the exception set yourself by looking at its signature? Put the call in a nothrow scope and compile the module (which is fast in D), the compiler will then complain which exceptions you didn't catch (requires improvement of nothrow analysis [1]). [1] http://forum.dlang.org/post/uovtkvpdagzagzhyacbp@forum.dlang.org [2] http://forum.dlang.org/post/lxejskhonjtiifvvgwnd@forum.dlang.org [3] http://forum.dlang.org/post/fjjkqbxiznlxfstqntnv@forum.dlang.org |
Copyright © 1999-2021 by the D Language Foundation