@rhymes wow! That's a very detailed and deep opinion. Thanks for bringing this up.
There's no way to tell which line of code will be executed after the exception is thrown.
Let me give you another example. Imagine that you have a big library / framework / project. And you are just debugging it. And then your execution flow jumps to some random place in the code with an exception thrown somewhere inside the method you were jumping over. "What the hell happened?" is what I usually have in my mind at this moment.
And that's what make me feel like we have two executional flows in the app. Regular and exception flows. The problem that I see in this separation that we have to wrap our minds against these two fundamentally different flows. First is controlled with regular execution stack and the second one is gotoish (as you called it). And it is controlled with except cases. The problem is that you can break this second flow by just placing some new awkward and accidental except case somewhere in between.
And, while I can perfectly live with the exceptions, I start to notice that my business logic suffer from it when it is growing in size. And having an extra way to work with different logical error in your program is a good thing.
it is not possible to wrap all your possible errors in special-case classes
I was talking about special classes like AnonymousUser is django. Not custom exception classes.
seems to veer quite a bit away from "Python's zen" which are quite explicit on the idea that errors shouldn't be silent
Unless explicitly silenced!
Jokes aside, we are not making them silent with wrapping into container values, just making them wrapped. Later you can use them anyway you want. You can reraise them if that's how it should be done.
Here you're telling the reader that the method can raise an exception
Yes, exactly! All functions may raise. But, some functions are most likely to do it: when dealing with IO, permissions, logic decisions, etc. That's what we indicate here. And, yes, my example with Result['UserProfile', Exception] should be rewritten as Result['UserProfile', ExactExceptionType].
When exceptions become the important part of your logic API - you need to use Result type. That's a rule that I have created for myself. Not talking about just python, but also elixir and typescript.
Thanks for taking a part in my rant, I have really enjoyed reading your response ;)
Let me give you another example. Imagine that you have a big library / framework / project. And you are just debugging it. And then your execution flow jumps to some random place in the code with an exception thrown somewhere inside the method you were jumping over. "What the hell happened?" is what I usually have in my mind at this moment.
I know what you're talking about, I've been there but to me is more an issue of abstraction than of exception handling. Some apps have too many deps :D BTW how is that super different from calling a function?
importtemplate_librarydefmethod_to_do_something():a="Hello world"result=template_library.render(a=a)# do something else...
returnresult
in this example I jump from method_to_do_something to template_library.render and back everytime I debug. And then if render calls yet another library the thing goes on. Sometimes you spend minutes stepping in libraries because the dependency tree is long (Ruby with open classes, heavy use of metaprogramming and DSLs sometimes is a nightmare to debug)
If you think about it, in a way, functions are labeled gotos with an address to go back to ;-)
and, yes, my example with Result['UserProfile', Exception] should be rewritten as Result['UserProfile', ExactExceptionType].
ok, that's make a little more sense :D
That's a rule that I have created for myself. Not talking about just python, but also elixir and typescript
Got it. Programmers are strange animals, when they find a tool or an idea they like they want to apply to everything :D.
Thanks for taking a part in my rant, I have really enjoyed reading your response ;)
ahhaha :D
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
@rhymes wow! That's a very detailed and deep opinion. Thanks for bringing this up.
Let me give you another example. Imagine that you have a big library / framework / project. And you are just debugging it. And then your execution flow jumps to some random place in the code with an exception thrown somewhere inside the method you were jumping over. "What the hell happened?" is what I usually have in my mind at this moment.
And that's what make me feel like we have two executional flows in the app. Regular and exception flows. The problem that I see in this separation that we have to wrap our minds against these two fundamentally different flows. First is controlled with regular execution stack and the second one is
goto
ish (as you called it). And it is controlled withexcept
cases. The problem is that you can break this second flow by just placing some new awkward and accidentalexcept
case somewhere in between.And, while I can perfectly live with the exceptions, I start to notice that my business logic suffer from it when it is growing in size. And having an extra way to work with different logical error in your program is a good thing.
I was talking about special classes like
AnonymousUser
isdjango
. Not custom exception classes.Unless explicitly silenced!
Jokes aside, we are not making them silent with wrapping into container values, just making them wrapped. Later you can use them anyway you want. You can reraise them if that's how it should be done.
Yes, exactly! All functions may raise. But, some functions are most likely to do it: when dealing with IO, permissions, logic decisions, etc. That's what we indicate here. And, yes, my example with
Result['UserProfile', Exception]
should be rewritten asResult['UserProfile', ExactExceptionType]
.When exceptions become the important part of your logic API - you need to use
Result
type. That's a rule that I have created for myself. Not talking about justpython
, but alsoelixir
andtypescript
.Thanks for taking a part in my rant, I have really enjoyed reading your response ;)
I know what you're talking about, I've been there but to me is more an issue of abstraction than of exception handling. Some apps have too many deps :D BTW how is that super different from calling a function?
in this example I jump from
method_to_do_something
totemplate_library.render
and back everytime I debug. And then ifrender
calls yet another library the thing goes on. Sometimes you spend minutes stepping in libraries because the dependency tree is long (Ruby with open classes, heavy use of metaprogramming and DSLs sometimes is a nightmare to debug)If you think about it, in a way, functions are labeled gotos with an address to go back to ;-)
ok, that's make a little more sense :D
Got it. Programmers are strange animals, when they find a tool or an idea they like they want to apply to everything :D.
ahhaha :D