Can Lisp be told not to enter the "break loop" when encountering an
error, but instead just return to the top-level?
I'm a newbie and I'm typing all kinds of junk into the top-level by way
of exploration. I don't want Lisp to enter the break loop when I type
in something nonsensical - I just want it to describe the problem and
return to top-level.
If this cannot be done in ANSI CL, then a means for doing it in clisp
would be most helpful.
thankyou,
Nick.
Nicholas Sandow <······@internode.on.net> writes:
> Can Lisp be told not to enter the "break loop" when encountering an
> error, but instead just return to the top-level?
(ignore-errors (do-something))
> I'm a newbie and I'm typing all kinds of junk into the top-level by
> way of exploration. I don't want Lisp to enter the break loop when I
> type in something nonsensical - I just want it to describe the problem
> and return to top-level.
>
> If this cannot be done in ANSI CL, then a means for doing it in clisp
> would be most helpful.
You can write your own REPL:
(defmacro handling-errors (&body body)
`(HANDLER-CASE (progn ,@body)
(t (ERR) (format t "~&~S~%~A~%" err err))))
(defun repl ()
(do ((hist 1 (1+ hist))
(+eof+ (gensym)))
(nil)
(format t "~%~A[~D]> " (package-name *package*) hist)
(handling-errors
(setf +++ ++ ++ + + - - (read *standard-input* nil +eof+))
(when (or (eq - +eof+)
(member - '((quit)(exit)(continue)) :test (function equal)))
(return-from repl))
(setf /// // // / / (multiple-value-list (eval -)))
(setf *** ** ** * * (first /))
(format t "~& --> ~{~S~^ ;~% ~}~%" /))))
[157]> (repl)
COMMON-LISP-USER[1]> (/ 1 0)
#<SYSTEM::SIMPLE-DIVISION-BY-ZERO #x204D3786>
division by zero
COMMON-LISP-USER[2]>
--
__Pascal Bourguignon__ http://www.informatimago.com/
What is this talk of 'release'? Klingons do not make software 'releases'.
Our software 'escapes' leaving a bloody trail of designers and quality
assurance people in it's wake.
Pascal Bourguignon wrote:
>>Can Lisp be told not to enter the "break loop" when encountering an
>>error, but instead just return to the top-level?
>
> (ignore-errors (do-something))
This is appropriate for almost any normal circumstance, but isn't
quite bulletproof.
ignore-errors is roughly equivalent to a handler-case for conditions
of type cl:error wrapped around the body. In CL errors are usually
signalled with the error function, which is defined approximately
(defun error (datum &rest arguments)
(let ((condition (_coerce-to-condition_ frob rest)))
(signal condition)
(invoke-debugger condition)))
The _coerce-to-condition_ function implements the hairy way condition
signalling functions interpret their arguments either to be a condition
or to be a designator for a condition. The condition is signalled, and
if some surrounding handler handles it (by not returning) the debugger
is not called. But if no surrounding handler handles the condition then
the debugger will be entered.
Here's an example how ignore-errors might not be adequate:
cl-user(5): (ignore-errors
(error (make-condition 'simple-error
:format-control "Hello, world!"
:format-arguments nil)))
nil
#<simple-error @ #x100178a5f2>
cl-user(6): (ignore-errors
(error (make-condition 'simple-warning
:format-control "Hello, world!"
:format-arguments nil)))
Error: Hello, world!
[condition type: simple-warning]
Restart actions (select using :continue):
0: Return to Top Level (an "abort" restart).
1: Abort entirely from this (lisp) process.
I think Pascal's answer is correct and quite sufficient in normal
circumstances and in the way the condition system is normally used,
but it isn't bulletproof in the face of strangely written
application code.
Steven M. Haflich wrote:
> Pascal Bourguignon wrote:
>
> >>Can Lisp be told not to enter the "break loop" when encountering an
> >>error, but instead just return to the top-level?
> >
> > (ignore-errors (do-something))
>
> This is appropriate for almost any normal circumstance, but isn't
> quite bulletproof.
It ignores too much on Allegro Common Lisp, which seems to consider
control-C to be an error.
(defmacro ignore-errors (&rest forms)
(let ((block-name (gensym "IGNORE-ERRORS-BLOCK-")))
`(BLOCK ,block-name
(HANDLER-BIND (#+allegro (EXCL:INTERRUPT-SIGNAL #'ERROR)
(ERROR (LAMBDA (CONDITION)
(RETURN-FROM ,block-name
(values NIL CONDITION)))))
,@forms))))
From: Christophe Rhodes
Subject: Re: Returning to top-level on error
Date:
Message-ID: <sqwthlogki.fsf@cam.ac.uk>
"Steven M. Haflich" <···@alum.mit.edu> writes:
> I think Pascal's answer is correct and quite sufficient in normal
> circumstances and in the way the condition system is normally used,
> but it isn't bulletproof in the face of strangely written
> application code.
Isn't the way to disable the debugger, well, to disable the debugger,
rather than to try to handle conditions of various types?
(defun probably-disable-debugger (c f)
(declare (ignore f))
(abort c))
(setf *debugger-hook* #'probably-disable-debugger)
Christophe
Christophe Rhodes <·····@cam.ac.uk> writes:
> "Steven M. Haflich" <···@alum.mit.edu> writes:
>
> > I think Pascal's answer is correct and quite sufficient in normal
> > circumstances and in the way the condition system is normally used,
> > but it isn't bulletproof in the face of strangely written
> > application code.
>
> Isn't the way to disable the debugger, well, to disable the debugger,
> rather than to try to handle conditions of various types?
>
> (defun probably-disable-debugger (c f)
> (declare (ignore f))
> (abort c))
> (setf *debugger-hook* #'probably-disable-debugger)
That will work, except for explicit calls to CL:BREAK, and assuming
your implementation doesn't do something annoying, like have another
*debugger-hook*-alike. Man, that took me long enough to figure out.
So, add to that:
#+sbcl (setf sb-ext:*invoke-debugger-hook* #'probably-disable-debugger)
--
/|_ .-----------------------.
,' .\ / | Free Mumia Abu-Jamal! |
,--' _,' | Abolish the racist |
/ / | death penalty! |
( -. | `-----------------------'
| ) |
(`-. '--.)
`. )----'
Christophe Rhodes wrote:
> Isn't the way to disable the debugger, well, to disable the debugger,
> rather than to try to handle conditions of various types?
>
> (defun probably-disable-debugger (c f)
> (declare (ignore f))
> (abort c))
> (setf *debugger-hook* #'probably-disable-debugger)
Well, invoking an abort restart returns to the innermost
reasonable command loop. This isn't generally the same thing
as wrapping an ignore-errors around some smaller block of code,
allowing that code silently to continue execution if the wrapped
code fails for any reason.
It all depends which is the desired behavior in the particular
context.
Globally disabling the debugger might be a very baaad thing to do.
Some debuggers have the ability to recover from otherwise fatal
errors, such as setting one of the cl special variables to some
illegal value. The debugger might upon entry check these variables
for reasonableness and lambda bind a reasonable default, allowing
debugging to be done.
Nicholas Sandow wrote:
> Can Lisp be told not to enter the "break loop" when encountering an
> error, but instead just return to the top-level?
>
> I'm a newbie and I'm typing all kinds of junk into the top-level by way
> of exploration. I don't want Lisp to enter the break loop when I type
> in something nonsensical - I just want it to describe the problem and
> return to top-level.
>
> If this cannot be done in ANSI CL, then a means for doing it in clisp
> would be most helpful.
There's probably a keystroke which allows you to quickly get to the top
level to make this less painful.
However, here is a function definition that gives you an idea how you
could approach it, if you really want to. (It's not perfect.)
(defun repl-without-errors ()
(tagbody
restart
(handler-case
(loop (print '>)
(print (eval (read))))
(error (c)
(print c)
(go restart)))))
CL-USER 5 > (repl-without-errors)
> (+ 4 5)
9
> (foo 8)
#<UNDEFINED-FUNCTION 100AEB37>
> (* 2 3)
6
>
Pascal
--
My website: http://p-cos.net
Closer to MOP & ContextL:
http://common-lisp.net/project/closer/