• Posts
  • RSS
  • ◂◂RSS
  • Contact

  • Exceptions in programming: asking forgiveness instead of permission

    October 26th, 2009
    programming, python  [html]
    In languages like C where there is nothing to check that what you're doing isn't going to explode on you, the normal way to deal with exceptional conditions would be to check first whether there is a problem:
           if (obj != NULL)
               work_with(obj->attr);
           else
               pester_user("obj can't be null!");
         
    In languages that run in an interpreter, such as Python or Java, it's possible for the interpreter to make sure you're not doing anything illegal and raise an exception if you do. In C, trying to dereference a null pointer is undefined; code may do absolutely anything afterwards. In Python and Java, though, you get an AttributeError and NullPointerException instead. This is because when you write a.b there is low level code that always runs and checks that a is not null for you. Very handy. This means I can write a python version as:
           try:
               work_with(obj.attr)
           except AttributeError:
               pester_user("obj can't be null!")
         
    The general idea that you should do something, then deal with the fallout if it fails, gets called "Easier to Ask Forgiveness than Permission".

    There are three reasons, however, why I tend to find it frustrating. One is that to make it work properly I need to know exactly which exception will be thrown if the command fails. So the python statement a[i] will throw an IndexError if a is a list or a KeyError if it is a dictionary. So I need to look or I need to remember. Ugh. Why make me do that? Can this be avoided?

    My second issue is that it can hide errors. What if a is an instance of a class I've written that implements __getitem__ to support a[i] syntax, and has an bug in it's implementation that manifests as an IndexError? My code won't see that IndexError as exceptional, but instead just thinks "ok, a is empty".

    My third issue is that it's ugly and verbose. I like to write in the 'permission getting' way with:

           if key in a:
               work_with(a[key])
           else
               pester_user("key not in a")
         
    while the 'forgiveness requesting' way feels much worse:
           try:
               work_with(a[key])
           except KeyError:
               pester_user("key not in a")
         
    (Note that this brings up another issue with basing programming on exceptions: if either a[key] or work_with might throw a KeyError, I can't separate them cleanly.)

    This is not actually a very good reason to dislike the second example, as it is no more verbose than the first one. Both are four lines, similar number of characters. They differ in that the first case requires me to know the proper check while the second case requires me to know the proper exception to catch. This shouldn't be import either. Do I just need to get used to the way try/except looks?

    I'm not sure I can fix the third (it's ugly!) issue, but I think the first two might be fixable. They come from not being able to accurately indicate what I would like to catch. If I see someone write:

           try:
               work_with(a[key])
           except KeyError:
               pass
         
    I see that what they intend is very likely "work with a[key] if it exists". Is there a way we could tell python that? Something like:
           try:
               work_with(<a[key] : bad_indexing>)
           except bad_indexing:
               pass
         
    then distinguishing errors raised by work_with from those raised by the lookup is clean:
           try:
               <work_with(<a[key] : bad_indexing>) : work_with_error>
           except bad_indexing:
               pass
           except work_with_error:
               pester_user("work with error")
         
    Still not too happy about this, though. Need to think more.

    Comment via: facebook

    Recent posts on blogs I like:

    Fireside Friday, November 27, 2020

    Hey folks! Fireside this week. A bit of a change-up in terms of the coming attractions. I had planned to start “Textiles, How Did They Make It?” next, but I want to do a bit more reading on some of the initial stages of textile production (that is, the pr…

    via A Collection of Unmitigated Pedantry November 27, 2020

    Building Depth and Window Space

    How much window space does an apartment need, relative to its area, and how does this affect building style? A fascinating post from about a year ago on Urban Kchoze makes the argument that modern North American buildings are too deep – Simon calls them o…

    via Pedestrian Observations November 27, 2020

    Thoughts you mightn't have thunk about remote meetings

    Welcome to this week's edition of "building a startup in 2020," in which all your meetings are suddenly remote, and you probably weren't prepared for it. I know I wasn't. We started a "fully remote" company back in 2019, but …

    via apenwarr November 23, 2020

    more     (via openring)


  • Posts
  • RSS
  • ◂◂RSS
  • Contact