Simple error handling in newLISP
When I first began to program newLisp, I was concerned that it lacked the structured error handling syntax of the imperative languages I was used to. As my software begins to mature and I add more sophisticated error handling, I find that newLisp’s simple functions result in cleaner, more expressive code.
When I write Python or PHP, I often find myself spending as much time writing complicated chains of exception handling code, trying to anticipate all possible types of errors ahead of time. The majority of the software I write is for the web. In reality, the vast majority of errors that can happen are due to programmer error, whether it be a forgotten semi-colon (in the case of PHP), or an unexpected input type from a form. What is more important than handling every type of error is that the problem can be quickly defined and located so that the end user does not experience an excessive outage.
newLisp’s simple functions are not only adequate for these tasks, they make them trivial. In Python or PHP, I must write a long series of catch/except structures to allow different handling of each type of error. With newLisp, it is as simple as a cond expression. Take this example, where I cause an error condition using the function, throw-error:
(define (error-prone) (throw-error "Hello from the error!")) (catch (error-prone) 'caught)
Now, the error message generated by the (error-prone) expression lives in ‘caught. Not only that, but the catch expression will return either true or nil, so that we can write:
if (catch (error-prone) 'caught) (println "We won't ever get here, because error-prone always throws an error") (cond ((some condition) (some response)) ((some other condition) (some other response)) (true (catch-all response for unhandled errors))))
In truth, I was surprised by how effective this was. I was so accustomed to defining my own exceptions and sending them up the chain to the appropriate handlers that I got lost in the syntax, so to speak. Error call-backs can be defined as lambdas within the cond statement or simply passed to the cond expression by value.
For global errors, (error-event 'lambda)
allows the definition of a general error handler. All uncaught errors will trigger this function, which accepts no arguments, but can access error information through functions like (error-text) and (error-number).
Imperative languages tend toward overly verbose solutions. This is due to the fact that the language attempts to speak human, rather than computer. Unfortunately, humans do not favor the concise, definitive syntax a computer demands. To solve this, strict, wordy structures are needed to express something that is neither as efficient or eloquent as a similar expression in a more logically formulated language like lisp.