lua-users home
lua-l archive

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


Recently I've been pondering how errors should be handled in Lua.

The standard method (used by Lua itself and most libraries) is that a function should return nil and a string describing the problem on most errors. I'm not clear when they should do this vs when error() should be used.

I find this method isn't terribly efficient. At least if the returned strings are constant, they get interned and so comparing strings is as fast and simple as comparing integers. But some functions add details into their errors, which then means they have to be parsed. E.g.:
> =io.open('nope', 'r')
nil    nope: No such file or directory    2

To determine the actual error now requires some string parsing which is a bit inefficient. And there are edge cases:
> =io.open('/home/rena/No such file or directory', 'r')
nil    /home/rena/No such file or directory: Permission denied    13
(so a simple err:find("No such file") would fail here)

I feel like it'd be more efficient if they returned an error code instead, or perhaps both - an error code *and* a string giving a human-readable error message. Though it feels awkward to return the human-readable message first, but the other way around breaks the "standard" that Lua libraries use.
In fact I notice that io.open in particular *does* appear to be returning some type of error code, though the manual doesn't mention this. (At least not in the description of io.open, but maybe somewhere?)

Of course once you're returning error codes, you need to assign names to them, because "2" and "13" aren't very descriptive, and it's important to be consistent. (i.e. if "2" means "No such file or directory" from io.open, it should mean that everywhere.) At least on POSIX-compliant systems, we have errno.h that defines a number of helpful error codes. These aren't 100% ideal though, because:
1) They mostly describe errors that can occur in OS calls. There aren't really useful error codes for things like "table expected, got number" or "malformed pattern".
2) The ISO C standard only requires EDOM, EILSEQ, and ERANGE to be defined. Beyond that, it's all system-dependent. In particular, Windows and embedded systems might not have errno.h at all.
3) As far as I know, these names aren't exposed to Lua scripts anywhere in the standard libraries.
4) People can't seem to make up their mind as to whether to return EFOO or -EFOO.

Also, returning nil and error information doesn't fit so well with functions that can return nil in a non-error condition, e.g. string.find. That makes me feel more like any error should be handled with error(), but that's not terribly efficient either, and wrapping things in pcall() can be ugly. (try/catch sure would be nice to have!)

error() has the same "issue", too: the standard is to return strings, which then need to be parsed (especially if you don't want to display the whole stack trace from xpcall() in your UI). I feel like it'd be nice if we returned tables (exception objects, if you will), but then once again we need to agree on standards - what fields the table should have, and what exactly they should contain. Personally I'd expect them to contain at least the source file, source line, error message, and error code, and stack traceback if xpcall() were used, but maybe there are other useful things to add.
Also to support that, error() would need to be patched to add those fields (and ideally accept error(ENOENT, "%s: no such file", filename) instead of error({errcode=ENOENT, errmsg=("%s: no such file"):format(filename)})), without breaking the stack trace.

I've often also felt like Lua could stand to have a standard "warning" function. By default warning() might be something like:
function warning(msg, ...) io.stderr:write(msg:format(...) .. '\n') end
but the application could redefine it to send the messages elsewhere or do nothing.

I wonder what the Lua community thinks of this? I feel like a lot of these points could be addressed by just having an "error" library, that provides some useful error codes, an error/exception function that returns a table, maybe some type of try(function_to_try, function_to_handle_errors), and other such utilities. But then it's necessary to decide on a standard list of error codes (especially you don't want to have to edit the list later and have some programs using EFOO and some using EBAR for the same case because EBAR didn't exist when they were written), and it would be troublesome if some of the libraries your program uses are using this and some aren't. But maybe it could be done?

--
Sent from my Game Boy.