PSA: dnSpy is a terrifyingly good .NET decompiler
-
@Gąska No.
-
@sockpuppet7 said in PSA: dnSpy is a terrifyingly good .NET decompiler:
I catch them where it makes sense to catch them.
But, but, You gotta’ catch them all!™︎
-
@pie_flavor I'm confused. How can a Rust guy say forcing programmers to check for errors is a bad thing?
-
@Gąska In Rust you simply add a method call or a
?
. You have to handle it, but it doesn't take a ton of noise.
Meanwhile, how many calls do you surround with catch_unwind?
-
@pie_flavor said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@Gąska In Rust you simply add a method call or a
?
.And several other things you forgot to mention, like the whole error type hierarchy and/or conversion functions. I heard there are several libraries that help reducing the boilerplate, but they still don't eliminate it entirely.
You have to handle it, but it doesn't take a ton of noise.
And how much noise does
int foo() throws FooException
take? Or a simple try...catch at couple spots in call stack? You're seriously overblowing the problem.
-
@Gąska said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@pie_flavor said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@Gąska In Rust you simply add a method call or a
?
.And several other things you forgot to mention, like the whole error type hierarchy and/or conversion functions. I heard there are several libraries that help reducing the boilerplate, but they still don't eliminate it entirely.
You have to handle it, but it doesn't take a ton of noise.
And how much noise does
int foo() throws FooException
take? Or a simple try...catch at couple spots in call stack? You're seriously overblowing the problem.Go is working on reducing the noise produced by its error handling:
-
@ben_lubar I hope they won't go the "random crashes everywhere" way of the unchecked exceptions.
-
@Gąska said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@ben_lubar I hope they won't go the "random crashes everywhere" way of the unchecked exceptions.
Nope, they're going the "allow try/catch" way, except with a construct that's like
if (someException != null) { throw someException; }
. I suggest reading the document I linked to. It looks a lot nicer than the current way Go looks.
-
@ben_lubar said in PSA: dnSpy is a terrifyingly good .NET decompiler:
I suggest reading the document I linked to.
I don't know where you've got this bizarre idea that I care what happens with Go.
-
@ben_lubar said in PSA: dnSpy is a terrifyingly good .NET decompiler:
Go is working on reducing the noise produced by its
error handlingfanbois:If only...
-
@pie_flavor Hmm, I see. So they decided that it's necessary to make it part of the function signature (because people really should catch that when using the function), but still picked an unchecked exception.
Seems like a weird design.
-
@masonwheeler said in PSA: dnSpy is a terrifyingly good .NET decompiler:
There's a joke about stuff frequently getting deleted and vanishing without a trace just waiting to be made here...
I don't catch exceptions just to throw them away if thats what you think. I care a lot about my error handling.
-
@Gąska said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@topspin said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@pie_flavor There's arguments for not having checked exceptions (that's why C# decided against them).
Laziness.
Checked exceptions are part of the method signature on java. So if a base class method doesn't throw an exception, you won't be able to do it in a derived class. This can make your life a bit harder.
And all the benefit if them is about zero IMO. The time I was maintaining lots of code on .net I never thought, hey, maybe this code would be better with those throws clauses java has. No, never.
-
@sockpuppet7 do you disagree with Blakeyrat that knowing what exceptions a function might throw would be fairly useful?
-
@sockpuppet7 oh, and if deriving class throws exception that base class doesn't throw, you're violating Liskov's Substitution Principle.
-
@ben_lubar said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@Gąska said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@pie_flavor said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@Gąska In Rust you simply add a method call or a
?
.And several other things you forgot to mention, like the whole error type hierarchy and/or conversion functions. I heard there are several libraries that help reducing the boilerplate, but they still don't eliminate it entirely.
You have to handle it, but it doesn't take a ton of noise.
And how much noise does
int foo() throws FooException
take? Or a simple try...catch at couple spots in call stack? You're seriously overblowing the problem.Go is working on reducing the noise produced by its error handling:
Yes, currently the noise of the programmer headdesking whenever they try to do it is quite loud.
-
@ben_lubar said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@Gąska said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@ben_lubar I hope they won't go the "random crashes everywhere" way of the unchecked exceptions.
Nope, they're going the "allow try/catch" way, except with a construct that's like
if (someException != null) { throw someException; }
. I suggest reading the document I linked to. It looks a lot nicer than the current way Go looks.Are they going to do it in a way similar to Rust, where a single operator unwraps values and propagates errors?
-
@masonwheeler said in PSA: dnSpy is a terrifyingly good .NET decompiler:
Yeah, this is where the "this is less of a horrifying action than it sounds like at first, due to architectural raisins too involved to get into here" part comes in. He explains elsewhere that Midori was designed with a "lightweight process" model, similar to the sort of stuff you see in Erlang, where it would be no big deal to spin off a process to deal with each web request, and having one of them crash would not involve crashing the server.
There are some down-side consequences to that too, particularly in management of state persistence and controlling the degree of isolation. This is definitely a bunch of trade-offs; whenever you make errors more easily trackable (at the language level; the implementation is a whole 'nother thing) you get faced with the reality that there's often a lot more errors than you really want to think about, yet make the errors more automatic (e.g., everything unchecked) then users don't track them and we end up with Blakey's problem of it being very hard to figure out what's going on in any complex piece of code.
Yes, a compiler (or rather one of its associated analysis tools) could figure it out in some cases, more so if it is running in the context of the actual execution and so is seeing all the concrete classes, but it is insanely difficult to do well and not end up with “anything could go wrong, anything at all” without investing in checked exceptions such as in Java. That's much better characterised in this aspect than C# because the language forced it from the beginning (at great overall cost, and some people hate this, of course) and as a consequence the handling of error cases is slightly better overall.
I don't see good ways of dealing with this. I do see a mixture of solutions from the reasonable ones (both Java and C# despite them being so different) to the thunderingly awful (error handling in most people's C code just makes my teeth itch, and I wouldn't be surprised in the slightest if a majority of practical Go code falls in this camp too despite not being quite as bad as C).
-
@sockpuppet7 said in PSA: dnSpy is a terrifyingly good .NET decompiler:
Checked exceptions are part of the method signature on java. So if a base class method doesn't throw an exception, you won't be able to do it in a derived class. This can make your life a bit harder.
It's less of a problem than it used to be, not since the general exception cause mechanism and multi-exception
catch
clause were added.
-
@pie_flavor said in PSA: dnSpy is a terrifyingly good .NET decompiler:
Looks like you'll never be using Integer.parseInt then.
That's not a very great example. You know whether what you're passing to that function is guaranteed to be an integer or not, so if the function throws an unexpected exception, that's on you.
-
@dfdub If you knew it was guaranteed to be an integer, then why do you need the function? are you talking about?
-
@pie_flavor said in PSA: dnSpy is a terrifyingly good .NET decompiler:
If you knew it was guaranteed to be an integer, then why do you need the function?
Because you still need to convert the string to an integer. Do you even know Java?
-
@dfdub And how, pray tell, do you know it's guaranteed to be an integer?
-
@Gąska said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@sockpuppet7 oh, and if deriving class throws exception that base class doesn't throw, you're violating Liskov's Substitution Principle.
Are we lawyering on solid principles now?
-
@pie_flavor said in PSA: dnSpy is a terrifyingly good .NET decompiler:
And how, pray tell, do you know it's guaranteed to be an integer?
Presumably by some higher-level constraint elsewhere (e.g., it was picked out of a bigger string by a regular expression that matched a digit sequence).
-
@dkf Under one particular use-case.
-
@sockpuppet7 said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@Gąska said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@sockpuppet7 oh, and if deriving class throws exception that base class doesn't throw, you're violating Liskov's Substitution Principle.
Are we lawyering on solid principles now?
SOLID principles exist for a reason.
-
@Gąska said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@sockpuppet7 said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@Gąska said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@sockpuppet7 oh, and if deriving class throws exception that base class doesn't throw, you're violating Liskov's Substitution Principle.
Are we lawyering on solid principles now?
SOLID principles exist for a reason.
The reason is "for lawyering on".
-
@Gribnit If you fuck up Liskov, you've fucked up, though.
-
@topspin Sure. Java won't let you, for checked exceptions. It inherently has to let you for runtime exceptions. Given implementation differences, you can either declare a checked catch-all for your delegates or let them violate LSP as far as errors go (unless you're already considering all runtime exceptions possible at all times as happens to be the case).
-
@Gribnit by runtime exceptions, do you mean exceptions thrown by runtime, or derivatives of
RuntimeException
(ie. all unchecked exceptions)?
-
@Gąska very probably the latter
-
@Gribnit probably? That was your own post!
-
@Gąska YMBNH
-
@Gribnit said in PSA: dnSpy is a terrifyingly good .NET decompiler:
Java won't let you, for checked exceptions. It inherently has to let you for runtime exceptions.
Effectively, Java says that all code
throws RuntimeException
(even if it doesn't actually) and you never have to declare it; because the API declaration is really at that level, the particular subclasses of it that are used are unimportant. (C# is that way for all exceptions.)
-
@Gribnit said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@topspin Sure. Java won't let you, for checked exceptions. It inherently has to let you for runtime exceptions. Given implementation differences, you can either declare a checked catch-all for your delegates or let them violate LSP as far as errors go (unless you're already considering all runtime exceptions possible at all times as happens to be the case).
It's not a LSP violation on C#. That rule is very subjective, and is about derived classes not breaking the expectations defined by the base class. In C# the base class should expect that any virtual method can throw any exception.
-
@sockpuppet7 said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@Gribnit said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@topspin Sure. Java won't let you, for checked exceptions. It inherently has to let you for runtime exceptions. Given implementation differences, you can either declare a checked catch-all for your delegates or let them violate LSP as far as errors go (unless you're already considering all runtime exceptions possible at all times as happens to be the case).
It's not a LSP violation on C#. That rule is very subjective, and is about derived classes not breaking the expectations defined by the base class. In C# the base class should expect that any virtual method can throw any exception.
In other words, C# tells that implication of LSP to go pound sand, and you all agree to agree with it.
-
@Gribnit I disagree with your view on how LSP works, but I think telling rule lawyers and cargo cultists to go pound sand* is a good thing, so you may be right.
(* whatever that means)
-
@sockpuppet7 said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@Gribnit said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@topspin Sure. Java won't let you, for checked exceptions. It inherently has to let you for runtime exceptions. Given implementation differences, you can either declare a checked catch-all for your delegates or let them violate LSP as far as errors go (unless you're already considering all runtime exceptions possible at all times as happens to be the case).
It's not a LSP violation on C#.
Depends. If the new exceptions are there because the derived class cannot be used in a certain way that the base class can be used in, then it violates LSP.
That rule is very subjective, and is about derived classes not breaking the expectations defined by the base class.
And expectations can be both explicit, checked expectations encoded with the programming language, or implicit, unchecked expectations that the programmer must ensure or the program will break in bizzare ways, but no one will tell him if they met those expectations or not. Both kinds of expectations must be met to fulfill LSP.
In C# the base class should expect that any virtual method can throw any exception.
And do you really think that's better design than explicitly stating what might blow up where and in what way?
-
@Gąska said in PSA: dnSpy is a terrifyingly good .NET decompiler:
And do you really think that's better design than explicitly stating what might blow up where and in what way?
Yes, I do. The throws clause was never useful for me, and it is annoying. That's all that matters.
I'm used to maintaining bad code. Compilers trying to enforce rules just create worse abominations.
-
@sockpuppet7 said in PSA: dnSpy is a terrifyingly good .NET decompiler:
I'm used to maintaining bad code. Compilers trying to enforce rules just create worse abominations.
You're sure you don't want to maintain PHP or NodeJS code?
-
@JBert the average php code is piece of cake compared with the lovecraftian horrors I have dealt with
-
@sockpuppet7 said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@Gąska said in PSA: dnSpy is a terrifyingly good .NET decompiler:
And do you really think that's better design than explicitly stating what might blow up where and in what way?
Yes, I do. The throws clause was never useful for me, and it is annoying. That's all that matters.
So you don't find it useful to know what errors you might get in a given function?
I'm used to maintaining bad code.
Have you ever worked with good code?
-
@Gąska said in PSA: dnSpy is a terrifyingly good .NET decompiler:
So you don't find it useful to know what errors you might get in a given function?
No, that was never really useful. I expect everything to return Exception
-
@Gąska said in PSA: dnSpy is a terrifyingly good .NET decompiler:
Have you ever worked with good code?
Yes. I'm sure it would be easier to find code we both agree that is bad than code we both agree is good, but I probably have seen it all at this point.
-
@sockpuppet7 said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@Gąska said in PSA: dnSpy is a terrifyingly good .NET decompiler:
So you don't find it useful to know what errors you might get in a given function?
No, that was never really useful. I expect everything to return Exception
Do you do that because you've been burnt too many times, or do you really think it's wrong to limit how many kinds of errors a given function might raise?
-
@Gąska said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@sockpuppet7 said in PSA: dnSpy is a terrifyingly good .NET decompiler:
@Gąska said in PSA: dnSpy is a terrifyingly good .NET decompiler:
So you don't find it useful to know what errors you might get in a given function?
No, that was never really useful. I expect everything to return Exception
Do you do that because you've been burnt too many times, or do you really think it's wrong to limit how many kinds of errors a given function might raise?
If you're already using C++, just throw a 64-bit integer, that way you can have 9,223,372,036,854,775,808 different exceptions and also satisfy everyone who prefers error codes.
-
@dfdub
Better make itunsigned long long
right away just in case there's not enough and someone starts using negative values and breaksif (Error < 1)
.
-
@Gąska said in PSA: dnSpy is a terrifyingly good .NET decompiler:
Do you do that because you've been burnt too many times, or do you really think it's wrong to limit how many kinds of errors a given function might raise?
I don't do anything, I'm just saying that throws clause never helped me when I used java, and I never missed it when using anything else. I don't approve of a compiler trying to impose those things on me. The compiler already has all the means to know what exceptions a function throws, I don't need to say it for it.
Imagine doing that without an IDE, you would end in a stupid cycle of compile, look what exception it complains about and updating the throws clause.
-
@sockpuppet7 said in PSA: dnSpy is a terrifyingly good .NET decompiler:
The compiler already has all the means to know what exceptions a function throws
No, it doesn't. That information isn't collected now, and is onerous to collect. Quite apart from the fact that some exceptions can happen anywhere at all, there are also a lot of operations that have failure modes that you don't want to have to deal with all the time. In particular, the
.
“operator” can always throw a null pointer exception and proving that it can't is messy as the language doesn't carry non-nullability around with it (at the moment). And it's hardly the only problem exception.Imagine doing that without an IDE
Don't write Java without an IDE. Or C#. You can get a reasonable one for zero cost, so just don't do without one. Other languages might not need an IDE, but those two definitely do.