lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]




On Friday, September 19, 2014, Sean Conner <sean@conman.org> wrote:
It was thus said that the Great Andrew Starks once stated:
>
> AND, I think i'm mostly talking about internals, anyway. The motivation of
> my question stemmed from having trouble dealing with errors in a sane way.
> How I deliver them to customers is somewhat (but I guess not really) of a
> different question, which should be more heavily influenced by conventions.

  I know my way of dealing with errors is somewhat controverial [1] and all
error handling schemes come down to three cases:

        1) return nil,error
        2) throw an exception
        3) ignore the error and proceed as if nothing happened (which on
           most systems nowadays will probably lead to #2 anyway)

  But I haven't noticed anyone approaching this from the other
angle---writing code as to avoid having to handle errors.

  When I write a library (for instance), I assume I will get valid data, and
I will try my darndest to return valid data to the caller.  So, for
instance, my DNS library [2], I have:

        dns_rcode_t dns_encode(
                dns_packet_t      *const restrict dest,
                size_t            *const restrict plen,
                const dns_query_t *const restrict query
        )
        {
          struct idns_header *header;
          uint8_t            *buffer;
          block_t             data;
          dns_rcode_t         rc;

          assert(dest  != NULL);
          assert(plen  != NULL);
          assert(*plen >= sizeof(struct idns_header));
          assert(query_okay(query));


  I assert() the parameters.  I am assuming you are giving me what I ask.
Yes, I could have done:

        if (dest == NULL)
          return RCODE_FORMAT_ERROR; /* or something more appropriate */

        if (plen == NULL)
          return RCODE_something;

        if (*plen < sizeof(struct idns_header))
          return RCODE_somethingelse;

        if (!query_okay(query))
          return RCODE_blahblahb;

but in my opinion, doing such "defensive programming" [3] hides bugs (why
would you be calling my routine with NULL pointers anyway?).  The calls to
assert() catch improper calls to the routine (and in at least two cases I
know of, actually caught bugs in other people's code using the library).
That's not to say errors still can't happen (you can still have bad domain
names, or not a big enough output buffer) but I feel like I've reduced the
list of potential errors.

  I do not use exceptions much, having come from Assembly and C which
doesn't really have exceptions [4], and because of that, I tend to do option
1 (and for throw-away stuff like simple scripts, option 3).  But in a
language that supports both (and you can do exceptions in Lua), I would
stick with the "return nil,error" method unless something comes up I didn't
expect and for which I have no idea how to handle, and thus, an exception
might be called for.  Maybe.

  Personally, I view "exceptions" like CPUs view "exceptions" (division by
0, trying to execute a priviledged instruction, or an unimplemented
instruction)---really bad programming errors that should cause the running
program to stop because there is no meaningful way for the program to
continue.  But that's me.

  -spc (Who doesn't like that signals have evolved past CPU exceptions)

[1]     Like from this exchange on the mailing list last year:

        http://lua-users.org/lists/lua-l/2013-12/msg00022.html

[2]     https://github.com/spc476/SPCDNS/blob/master/src/codec.c

[3]     I do have one story about being too defensive in coding.  At work,
        the senior developer, in certain critical portions of code, blocked
        all signals from being delivered (POSIX system and all).
        Occasionally, the code would a SIGSEGV (hitting bad memory) but it
        was hard to track down the exact cause, *because he blocked all
        signals in certain portions of the code!* The actual cause was in a
        critical section, but it was hard to catch because it was never
        generated where it actually happened.  He grumbled, but eventually
        fixed the code to be "less defensive."

[4]     For assembly, it really depends upon both the architecture and
        operating system; for C, you can kind of fake exceptions with
        setjmp()/longjmp() but it's hard to get right.


Good stuff, because you've revealed to me that I likely have a child's understanding of exceptions and their true nature.  I'm due for one or two Wikipedia explosions. 

Think on D and in Perl were well worth it, too. 

-Andrew