Download presentation
Presentation is loading. Please wait.
1
Exception patterns in Lua
Exceptions are underutilized in Lua. Let's change that. “In Lua” means core, standard libs, 3rd party libs, and applications. John Belmonte Lua Workshop 2006
2
Overview A reintroduction to exceptions Lua and exceptions
A simple try-except construct Custom error objects
3
What problem do exceptions solve?
Reasonable program behavior despite lack of error handling Error handling only where needed Consistency in raising and handling Simpler API's (Good summary at “Handling” is checking, response, and propagation Reasons for no error checking: quick and dirty scripting; oversight; programmer error Excellent overview of error handling in D Language documentation Consistency: e.g. vs. C where errors are signaled in many ways (return true/false, false/true, negative number, or store in a global) Simple API's by functions not returning errors, no need for error code to string conversion support.
4
Exception Concepts Raise Catch Re-raise Selective catch
can apply to any catch scenario requires classification of errors Exceptions are part of an API Re-raising and selective catching are not addressed in PIL. The more exceptions are used, the more selective catch ability is needed.
5
Usage Scenarios Quick scripting let everything go unhandled
Catching errors for: suppression alternate code path cleanup (often re-raising) retry transformation (always re-raising) add context hide implementation Error handling is tedious. Exceptions let you defer error handling to later in development, e.g. by addressing exceptions as they are encountered. Catching examples: Read of config file fails, so fallback to internal default. HTTP library catches socket error, adds details about HTTP state (e.g. “error fetching headers”) program deletes its temporary files on any exception retry transaction when there is a database read conflict Transform catch can be used to hide implementation details (e.g. avoid exposing exceptions from modules used in implementation).
6
What should be an error? Obvious error: invalid arguments
Usually not an error: string match failure What about file operation failures (open, delete, rename)? Criteria: If caller usually can't deal with the situation locally, it's an error i.e. errors usually propagate up two or more stack frames Finding the right answer to this is important for correct use of exceptions. Put another way, exceptions are most useful for propagating errors up two or more stack frames. If the situation is usually handled by the immediate parent, communicating by function return value makes more sense. I don't agree with PIL regarding io.open(). PIL guideline is that if exceptional situation cannot be easily avoided, it should return nil rather than raise error. So since it's hard to know if io.open will fail before calling it, it shouldn't raise an error. That view may have been influenced by lack of error classification in Lua.
7
Lua and exceptions Raise with error(), assert(), lua_error()
Catch with pcall() Implemented with C longjmp() Error object not limited to strings No try-except construct Implementation with C++ exceptions is a configuration option. The story with Lua and exceptions sounds simple, but...
8
Usage in core and standard library
Exceptions mainly used for obvious programming errors parse errors type errors invalid function arguments Notable departures: require(), dofile() Exclusively string error objects
9
The nil-error protocol
On error, function returns [nil, error message] tuple Made popular by Lua standard libs Issues not checking can result in delayed, secondary error what if nil is a valid output? Can use assert() to convert to exception This protocol is the anti-exception pattern. Example of nil as valid output: lookup on table implementing permissioning. Returning nil for “permission denied” conflicts with nil result of nonexistent table entry. Not correctly propagating an error can also cause problems. Wrapping with assert() obviously assumes a string error object. It also precludes concept of assert as a unique type of error (i.e. as declaration by programmer that case is not expected).
10
A simple try-except construct
Rationale useful familiar encourages use of exceptions Requirements usable without Lua changes can be nested
11
Try-except definition
try(f, catch_f) Executes function f, and if an exception results then catch_f is called with the error object. Differs from xpcall() propagates exceptions rather than returning nil-error error handler supports nested errors On exception within error handler, xpcall summarily returns “error in error handling” as error message.
12
Try-except implementation and usage
function try(f, catch_f) local status, exception = pcall(f) if not status then catch_f(exception) end try(function() -- Try block -- end, function(e) -- Except block. E.g.: -- Use e for conditional catch -- Re-raise with error(e) end)
13
Try-except issues Slightly verbose
use token filter: $try ... $catch(e) ... $end Functional implementation doesn't support direct return from try/catch native implementation would solve this Coroutine yield cannot be used within a pcall copcall() is a workaround Add finally block? not as significant as for C D's “scope hook” concept is better In token filter implementation, I suggest “$” prefix for keywords for two reasons: makes it clear that such keywords are not built-in greatly simplifies filter implementation (i.e. it doesn't need to be aware of parse tree) Unfortunately try and catch keywords haven't been reserved in Lua. Lua is all about mixing simple, powerful mechanisms. The inability to mix protected calls and coroutines is a large wart. copcall() is from the coxpcall project on LuaForge. The D Language scope hook is described in <
14
Custom exception objects
error({code=121}) What's wrong with strings? selective catch is fragile at best Tables as errors positive error identity can attach arbitrary context Classes as errors can employ inheritance testing The table throw expression is from PIL. Human-readable message should be just one part of richer object. Python abandoned strings as exceptions in version 1.5 (1998). Standard lib implementation makes string exceptions ubiquitous (luaL_error, assert). The arbitrary context is stored at raise time (by code that knows the most details about the error). Examples: store state related to error; store original exception when transforming. Inheritance test can be used to catch any one of a class of errors (e.g. any class derived from ArithmeticError).
15
Sample error hierarchy
Excerpt from Python's built-in hierarchy: Exception StandardError ArithmeticError FloatingPointError OverflowError ZeroDivisionError AssertionError ImportError KeyboardInterrupt RuntimeError NotImplementedError SyntaxError TypeError ValueError ImportError equivalent of Lua require() exception. Assertions are a distinct type of error. Used correctly, they signal that the programmer really didn't expect or properly handle some case.
16
Uncaught table In the dark if table object is uncaught
> error({code=121}) (error object is not a string) In the dark if table object is uncaught what is the error? where did it come from? Call stack should be displayed regardless of error type lua.c should call tostring() on error objects All exceptions should have human-readable message Call stack only displayed for string errors due to detail in lua.c. It is passing non-string as message arg of debug.traceback(), and in that case debug.traceback() simply returns that arg] Fixing lua.c to call tostring is a trivial patch and is harmless enough to consider for Lua Aside: would like a luaL_tostring, which honors the __tostring hook.
17
Still missing file and line number!
A better error object Set __tostring hook Make reference available _exception_mt = { __tostring = function(e) return 'ERROR: '..e.msg end } SomethingBad = {code=121, msg = 'Oops' } setmetatable(SomethingBad, _exception_mt) Then, with patched lua.c: > error(SomethingBad) ERROR: Oops stack traceback: [C]: in function 'error' ... This __tostring definition would be better, but there is no rawtostring(): __tostring = function(e) return e.msg and 'ERROR:'..e.msg or rawtostring(e) end We aren't there yet-- we have a call stack but the location (file & line number) is still not reported. Still missing file and line number!
18
How error locations are conveyed in Lua
Error system does not have concept of error location Convention is to pre-pend location to error string error() does this for you ... but only for string exceptions
19
Error location fix Ideal: lua_error() associates location with error object possible efficiency concerns Compromise: error() sets location directly on object when it's a table prototyped, works well
20
Conclusions Throw exceptions in situations which usually can't be handled locally by parent stack frame Use try-except construct for exception handling Throw tables instead of strings Enumerate errors as part of API Fixing pcall/coroutine problem is important Standard interface for inheritance testing would be useful Be sure that tostring() on error object provides human-readable message. Throwing tables and enumerating errors applies to Lua core/standard libraries as well as 3rd party libs. Classifying errors and knowing when to throw is especially important for library design. Mark Hamburg: “My number one request [for Lua 5.2] is to make pcall yield friendly or provide an alternative error handling mechanism that is yield friendly.” Diego Nehab says pcall/coroutine problem keeps him from adopting exceptions in his socketlib.
21
Resources See presentation source for ample notes, bonus slides
Power patch for custom error object support coming soon Other work on Lua and exceptions: Programming in Lua, chapter 8. Diego Nehab's “Finalized Exceptions” LTN This article seems to promote the nil-error protocol, but Diego informs me that was not his intent.
22
(bonus slides follow)
23
Abusing exceptions Avoid non-error uses inefficient confusing
E.g. as cross-stack goto to propagate result up several stack frames as “break” in a nested computation Unfortunately Python iterators employ a “stop iteration” exception.
24
Exceptions and library design
Fatal to library ≠ fatal to library user never call os.exit() no such thing as “exception which shouldn't be selectively caught”
Similar presentations
© 2025 SlidePlayer.com Inc.
All rights reserved.