lua-users home
lua-l archive

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


On 19 September 2014 16:39, Andrew Starks <andrew.starks@trms.com> wrote:
>
>
> On Fri, Sep 19, 2014 at 12:12 PM, Hisham <h@hisham.hm> wrote:
>>
>> On 19 September 2014 13:18, Jorge <xxopxe@gmail.com> wrote:
>> > On 09/18/2014 01:18 AM, Andrew Starks wrote:
>> >>
>> >> I've been thinking more about this topic.
>> >>
>> >> So, the above quote from the illustrious Dirk seems to be "what smart
>> >> people do." That is, there is a consensus around this view.
>> >>
>> >> I don't think I understand it and I'm starting to think that "nil,
>> >> error_msg" has no place in my code.
>> >>
>> >> The problem with `nil, error` is that "reasonably expect" is both
>> >> unknowable and possibly irrelevant:
>> >
>> >
>> >
>> > In my mind, having a library crash the users program emitting an error()
>> > is
>> > not well behaved. As a library you don't know how important is whatever
>> > you
>> > are doing for the host program. Perhaps it doesn't particularly care if
>> > you're unable do do you task.
>> >
>> > So, who has to agressivelly pcall is the library.
>> >
>> > Also, as a final user, I find "nil,error" apis much easier to use.
>>
>> Agreed. When I get a third-party library to integrate with my code
>> (say, I need to support the FOO protocol and I found a lua-foo module
>> in LuaRocks that already implements it), I don't expect it to _ever_
>> force me to pcall.
>>
>> Just like the policy Rena mentioned, I heartily accept that it may
>> error() if I'm using lua-foo wrong. But not if it's a call that, for
>> some reason, may fail. For example, if you're implementing a file-like
>> interface with an :open() method, then failing to open (something that
>> may happen because of the user's environment and which I must check in
>> my code) should return `nil, errmsg` and not crash with error(). On
>> the other hand, if it takes either "r" or "w" as a second argument, I
>> expect it to error() if I give it "z".
>>
>> -- Hisham
>>
>
> Hisham / Jorge:
>
> How do you deal with the following:
>
> 1: You parse the input that you receive and you know it to be well-formed.
> 2: You call a library function that will look for a resource by that name
> and, if it fails, returns `nil, error`.
> 3: That same call may also generate an error. For example, perhaps the
> object contains a dependent resource that is in an invalid state.
>
> I can guess at what the answer to this question is: "if you return nil,
> error_msg, always return nil, error_msg" So, then the onus is on me to call
> pcall if there is any risk that an error would result.
>
> Is that correct?

Well, the way I see it is that you have to decide whether you're going
to use error+pcall as an awkward way of doing try+catch, or as a
"segfault handler" kind of thing.

In the LuaRocks code, for example, I use mostly the latter. I just
looked at all instances of pcall in the code:

* There's a bunch of them that are there only to implement the "try to
require" pattern, for optional features (such as `lfs_ok, lfs =
pcall(require, "lfs")` to see if we have a module available).
* Then there's a couple of pcalls that catch lfs errors
* I also have a couple of pcalls in my code that load()s the rockspec
files, to catch user syntax errors
* And then I have the _one big xpcall_ near the very top of my control
flow that acts the way a "segfault handler" would do on a C program.
It is there to catch my own programming errors and shows a message
saying "LuaRocks 2.x bug, please report etc etc" plus a stack trace.

So, in short, if I get an error() anywhere, that means I have a bug in
my code. I use assert() to check the types of practically every input
argument.

I looked at instances of error() in my code, and it features only
three times (and I did not write two of them). The only one I wrote is
inside the LuaRocks package loader module, which is a place where we
basically can't recover from in case of errors.

I also checked the behavior of io.open in the standard library, so we
could get a hint on how Lua itself handles this, and I learned
something interesting:

In Lua 5.1:

Lua 5.1.5  Copyright (C) 1994-2012 Lua.org, PUC-Rio
> print( io.open("/etc/passwd", "w") )
nil    /etc/passwd: Permission denied    13
> print( io.open("/etc/passwd", "zoo") )
nil    /etc/passwd: Invalid argument    22
> print( io.open() )
stdin:1: bad argument #1 to 'open' (string expected, got no value)
stack traceback:
    [C]: in function 'open'
    stdin:1: in main chunk
    [C]: ?
>

In Lua 5.2:

Lua 5.2.3  Copyright (C) 1994-2013 Lua.org, PUC-Rio
> print( io.open("/etc/passwd", "w") )
nil    /etc/passwd: Permission denied    13
> print( io.open("/etc/passwd", "zoo") )
stdin:1: bad argument #2 to 'open' (invalid mode)
stack traceback:
    [C]: in function 'open'
    stdin:1: in main chunk
    [C]: in ?
> print( io.open() )
stdin:1: bad argument #1 to 'open' (string expected, got no value)
stack traceback:
    [C]: in function 'open'
    stdin:1: in main chunk
    [C]: in ?
>

Notice the difference in the second case. Lua 5.2 behaves more in line
with the policy I described: when it's a coding bug (the programmer
messed up), it fails with error(); when it's an actual failure in
operation, we get `nil, errmsg`.

-- Hisham