[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: traceback and error handling pitfalls (5.2)
- From: David Manura <dm.lua@...>
- Date: Sat, 21 Aug 2010 13:38:00 -0400
Lua's error handling [9] has some design concerns. The most
comprehensive assessment was given by John in Lua Gems [1], but I have
a few additional comments.
First, Lua 5.2.0work4 makes a couple small strides in this area, such
as xpcall arguments, yields across pcalls [3], and the Lua interpreter
invoking tostring on error objects, although noticeably absent is some
improvement to code execution on scope exit [2] beyond the functional
style solution in [1].
The area I'd like to further highlight though is the irregular
treatment of tracebacks. `error(message, level)` prefixes message
with the filename/source+linenumber if message is a string (or number,
which is convertible to a string) and level is not explicitly set to
0. Normally it is prefixed since most Lua errors are strings and
level either defaults to 1 or the programmer wants a positive level.
On the other end of this, this seems to be more properly the
responsibility of the error handler. The default error handler,
debug.traceback, *appends* the full traceback to this error object if
it is a string (or number). pcall, however, doesn't propagate this
current error handler (and there's no way to query it, even with the
debug library--shouldn't there be?), so inside a pcall the
filename/source+linenumber is prefixed but the traceback is not
appended. xpcall, improved in 5.2, can set an error handler, but to
set it to debug.traceback you need to access the debug library, and
there's a principle that the debug library is to be avoided in normal
code [4]. Furthermore, nested pcalls pose the danger of obtaining a
double traceback [5]. The problem of extracting meaning from an error
string [1] may also be complicated by the surrounding traceback info.
Even extracting the traceback from the error string is unreliable (IMO
unnecessarily) by the paths in the traceback being truncated [6]. The
solution mentioned in [1] was the power patch [7] modifying
debug.traceback to inject the traceback into a field in any error
object that is a table, although a less invasive technique would be to
link these objects via a shared weak table. As discussed, however,
any method like this only works for error objects with a unique
identity, which excludes strings. Apart from requiring error objects
to have a unique identity with a certain way to associate with a
traceback, the ideal solution in the opinion of [1] is for pcall to
maintain the traceback independently of the error object. Finally, as
discussed [10], the level parameter on error is not easy to use
correctly and falls in a similar boat with stack-level aware functions
like the deprecated getfenv. IMO, these issues appear too numerous.
The other area I'll mention concerns the interfaces of the closely
related assert and pcall functions. I've mentioned before that assert
is reused in Lua in an idiomatic way, opposite to pcall, that is
convenient but maybe unconventional [8]. The footnote #1 in [1]
mentions also that "[wrapping a function in assert] can't be used,
however, if nil or false happen to be valid outputs. It can also
interfere with code readability when a function has multiple outputs
and the caller elects not to wrap with assert (e.g., a function
returns coordinates x and y, but on error y doubles as a message)." A
similar thing I find happens with pcall as in `local ok, err_a, b =
pcall(f)`. Here, ok contains the status code. However, when ok is
false, err_a is the error value (err), whereas when ok is true, err_a
is the first return value (a). This complicates properly naming
`err_a` for readability. It would seem better to me for pcall to work
like `local err, a, b = pcall(f)` where err is the error object. This
will require that nil and false no longer be used as error objects,
which are odd in any case, or at least that pcall converts them to
some other value like true. (Incidentally, `error(nil)` is silent in
both 5.1 and 5.2.0-work4 and `error(false)` displays "(no error
message)", which may be undesirable.)
[1] Lua Programming Gems, Chapter 13 "Exceptions in Lua", by John Belmonte
[2] http://lua-users.org/wiki/ResourceAcquisitionIsInitialization
[3] http://lua-users.org/wiki/PcallAndCoroutines
[4] http://article.gmane.org/gmane.comp.lang.lua.general/65405
[5] http://article.gmane.org/gmane.comp.lang.lua.general/63896
[6] http://thread.gmane.org/gmane.comp.lang.lua.general/20578
[7] http://lua-users.org/wiki/LuaPowerPatches "custom errors"
[8] http://article.gmane.org/gmane.comp.lang.lua.general/65765
[9] http://lua-users.org/wiki/ErrorHandling
[10] http://article.gmane.org/gmane.comp.lang.lua.general/62224