Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Debugging Lisp code (stupid newbie question)

541 views
Skip to first unread message

Joe Knapka

unread,
Sep 5, 2004, 10:37:06 PM9/5/04
to
Hi everyone,

I am getting into Lisp again after a brief exposure in college,
followed by a lengthy absence (> 10 years). I seem to be missing the
blindingly obvious. I've now got a few CLs installed on my Linux box
(gcl, clisp, sbcl), and after poking around for hours in the man
pages, this group's archive and FAQ, Google, and Paul Graham's "ANSI
Common Lisp", I haven't been able to find any way to set a breakpoint,
in any of the CLs I've installed. (trace) is nice, but I'd like a way
to say, "please stop any time my-func is evaluated," and then end up
at a prompt where I can see what's about to be evaluated, step over or
into forms, inspect objects, and so forth. Does such a thing exist?

Please forgive me if this is a FAQ. A pointer to relevant online
documentation would be more than sufficient.

Thanks,

-- Joe

--
"We sat and watched as this whole <-- (Died Pretty -- "Springenfall")
blue sky turned to black..."
... Re-defeat Bush in '04.
--
pub 1024D/BA496D2B 2004-05-14 Joseph A Knapka
Key fingerprint = 3BA2 FE72 3CBA D4C2 21E4 C9B4 3230 94D7 BA49 6D2B
If you really want to get my attention, send mail to
jknapka .at. kneuro .dot. net.

Pascal Bourguignon

unread,
Sep 5, 2004, 11:14:20 PM9/5/04
to
Joe Knapka <jkn...@kneuro.net> writes:

> Hi everyone,
>
> I am getting into Lisp again after a brief exposure in college,
> followed by a lengthy absence (> 10 years). I seem to be missing the
> blindingly obvious. I've now got a few CLs installed on my Linux box
> (gcl, clisp, sbcl), and after poking around for hours in the man
> pages, this group's archive and FAQ, Google, and Paul Graham's "ANSI
> Common Lisp", I haven't been able to find any way to set a breakpoint,
> in any of the CLs I've installed. (trace) is nice, but I'd like a way
> to say, "please stop any time my-func is evaluated," and then end up
> at a prompt where I can see what's about to be evaluated, step over or
> into forms, inspect objects, and so forth. Does such a thing exist?
>
> Please forgive me if this is a FAQ. A pointer to relevant online
> documentation would be more than sufficient.

Another useful trick is the function APROPOS:

> (APROPOS 'DEBUG)
==> a lot of symbols, and eventually:
INVOKE-DEBUGGER function


You can invoke it minimally with:

(invoke-debugger (make-condition 'error))

Unless you have a sophisticated IDE, you'll have to insert this call
manually in the functions where you want to break in. For example:

(defun bp () (invoke-debugger (make-condition 'error)))

(defun fact (n) (do ((n n (1- n)) (f n (* n f))) ((= 0 n) f) (bp)))


BUT, I never had to use the debugger like this in my Lisp programming
because I use rather a bottom-up building mode: I start with a simple
sexp at the REPL, and when I got a reasonable intermediary result, I
encapsulate it in a bigger sexp and up and up until I've got the
result I need, or until it's big enough to be called a function (and
then I proceeed with a new sexp invoking this new function).
Generally, the functions written this way are bug free, because
they've been debugged while being constructed, at the REPL.


--
__Pascal Bourguignon__ http://www.informatimago.com/

Our enemies are innovative and resourceful, and so are we. They never
stop thinking about new ways to harm our country and our people, and
neither do we.

Coby Beck

unread,
Sep 5, 2004, 11:36:22 PM9/5/04
to

"Pascal Bourguignon" <sp...@mouse-potato.com> wrote in message
news:874qmcf...@thalassa.informatimago.com...

> Joe Knapka <jkn...@kneuro.net> writes:
> > Common Lisp", I haven't been able to find any way to set a breakpoint,
> > in any of the CLs I've installed. (trace) is nice, but I'd like a way
> > to say, "please stop any time my-func is evaluated," and then end up
> > at a prompt where I can see what's about to be evaluated, step over or
> > into forms, inspect objects, and so forth. Does such a thing exist?
> >
> > Please forgive me if this is a FAQ. A pointer to relevant online
> > documentation would be more than sufficient.
>
> Another useful trick is the function APROPOS:
>
> > (APROPOS 'DEBUG)
> ==> a lot of symbols, and eventually:
> INVOKE-DEBUGGER function
>
>
> You can invoke it minimally with:
>
> (invoke-debugger (make-condition 'error))

What in the world is wrong with BREAK?

--
Coby Beck
(remove #\Space "coby 101 @ big pond . com")


Peter Seibel

unread,
Sep 5, 2004, 11:36:50 PM9/5/04
to
Joe Knapka <jkn...@kneuro.net> writes:

> Hi everyone,
>
> I am getting into Lisp again after a brief exposure in college,
> followed by a lengthy absence (> 10 years). I seem to be missing the
> blindingly obvious. I've now got a few CLs installed on my Linux box
> (gcl, clisp, sbcl), and after poking around for hours in the man
> pages, this group's archive and FAQ, Google, and Paul Graham's "ANSI
> Common Lisp", I haven't been able to find any way to set a
> breakpoint, in any of the CLs I've installed. (trace) is nice, but
> I'd like a way to say, "please stop any time my-func is evaluated,"
> and then end up at a prompt where I can see what's about to be
> evaluated, step over or into forms, inspect objects, and so forth.
> Does such a thing exist?

Try the BREAK function. As in, change my-func to this:

(defun my-func (args)
(break "Entering my func with args: ~a" args)
...)

where the '...' is the original code. Now when my-func is called
you'll enter the debugger. What exactly you can do there depends on
the impl but all the things you mentioned are typically available. The
only thing to watch out for is the backtrace may contain a bunch of
stuff that isn't your code--particularly if your code is running in an
interpreter some of the stack frames will be the interpreter itself.
And in some environments (such as the excelent SLIME for Emacs) the
debugger itself may add a few frames above your code. But if you look
for the function you broke from the neighboring frames will be the
ones you're interested in.

BTW, while may seem primitave to some mouse-addicted folks, compared
IDE's that let you click on a line to insert a break point, having the
debugger built into the languge is actually quite powerful--you could
just as well write:

(defun my-func (args)
(when (some-condition-i-care-about args)
(break "Entering my func with args: ~a" args))
...)

> Please forgive me if this is a FAQ. A pointer to relevant online
> documentation would be more than sufficient.

So I haven't yet written about debugging in Lisp but you might find
some useful stuff in the draft chapters of the book I'm working on.
They're available at:

<http://www.gigamonkeys.com/book/>

If you have a chance to look at this and have any comments about what
you find useful and/or confusing I'd love to hear them.

-Peter

--
Peter Seibel pe...@javamonkey.com

Lisp is the red pill. -- John Fraser, comp.lang.lisp

Peter Lewerin

unread,
Sep 6, 2004, 4:18:29 AM9/6/04
to
Joe Knapka <jkn...@kneuro.net> wrote

> Common Lisp", I haven't been able to find any way to set a breakpoint,

(break)
(break "optionally with ~A (~A, ~A)" "format arguments" foo bar)

This puts you in the debugger, and prints the string if given any.
You can then use your system's continue command in which case the
function returns NIL.

> ... Re-defeat Bush in '04.

Yes, please stop this nightmare.

Pascal Bourguignon

unread,
Sep 6, 2004, 4:28:18 AM9/6/04
to

"Coby Beck" <cb...@mercury.bc.ca> writes:
> > INVOKE-DEBUGGER function

> What in the world is wrong with BREAK?

That I haven't completely read the Condition chapter yet...

Timothy Moore

unread,
Sep 6, 2004, 6:11:00 AM9/6/04
to
Peter Seibel <pe...@javamonkey.com> writes:

> Joe Knapka <jkn...@kneuro.net> writes:
>
> > Hi everyone,
> >
> > I am getting into Lisp again after a brief exposure in college,
> > followed by a lengthy absence (> 10 years). I seem to be missing the
> > blindingly obvious. I've now got a few CLs installed on my Linux box
> > (gcl, clisp, sbcl), and after poking around for hours in the man
> > pages, this group's archive and FAQ, Google, and Paul Graham's "ANSI
> > Common Lisp", I haven't been able to find any way to set a
> > breakpoint, in any of the CLs I've installed. (trace) is nice, but
> > I'd like a way to say, "please stop any time my-func is evaluated,"
> > and then end up at a prompt where I can see what's about to be
> > evaluated, step over or into forms, inspect objects, and so forth.
> > Does such a thing exist?
>
> Try the BREAK function. As in, change my-func to this:
>
> (defun my-func (args)
> (break "Entering my func with args: ~a" args)
> ...)
>

Also, TRACE in some implementations takes extra arguments that let you
break before and functions conditionally. The TRACE in SBCL, one of
the implementations you mention, has a large number of options; try
typing (documentation 'trace 'function).

Tim

Peter Lewerin

unread,
Sep 6, 2004, 7:47:22 AM9/6/04
to
Peter Seibel <pe...@javamonkey.com> wrote

> Lisp is the red pill. -- John Fraser, comp.lang.lisp

You know, the other day I was eating a juicy and delicious steak, and
it made me think about your .sig. It's getting close to twenty years
since I first used Lisp, and some eleven of those years I've used
ALGOL-style languages. After nine years, you know what I realize?

Lisp isn't always doing what the Man tells you, fighting unkillable
bugs, being tired and cold, eating the same goddamn goop everyday...

Lisp is bliss. No more trying to make sense of C or Perl alphabet
soup: all I see now is blonde, brunette, redhead.

Lisp isn't the red pill. It's the blue pill.

Lisp. What a mind-job.

niko...@random-state.net

unread,
Sep 6, 2004, 8:39:03 AM9/6/04
to
Joe Knapka <jkn...@kneuro.net> wrote:

> Common Lisp", I haven't been able to find any way to set a breakpoint,
> in any of the CLs I've installed. (trace) is nice, but I'd like a way
> to say, "please stop any time my-func is evaluated," and then end up

The following should work in SBCL and CMUCL:

(trace my-fun :break t)

For extra goodness

(trace my-fun :break t :wherein 'other-fun)

will break only if the call to my-fun occurs within the dynamic scope
of a call to other-fun.

CMUCL should allow you to start stepping from the debugger, SBCL doesn't
yet, but if all goes to plan a new stepper that will allow this will be
merged this month.

Cheers,

-- Nikodemus "Not as clumsy or random as a C++ or Java.
An elegant weapon for a more civilized time."

Joe Knapka

unread,
Sep 6, 2004, 10:44:09 AM9/6/04
to
Hi Peter,

Peter Seibel <pe...@javamonkey.com> writes:

> Joe Knapka <jkn...@kneuro.net> writes:

> [How do I set a breakpoint?]

> Try the BREAK function. As in, change my-func to this:
>
> (defun my-func (args)
> (break "Entering my func with args: ~a" args)
> ...)

My first reaction to this is, "You mean I have to edit my source and
recompile to set a simple breakpoint?!?" I am, perhaps, spoiled, but
sheesh, even gdb permits me to non-intrusively set a breakpoint. And
if I hit a breakpoint, notice some fsck'd data further up the call
chain, decide I need to break somewhere in the fsck'd frame and
restart the test case... I have to recompile my code to do that?
That seems like a serious speed-bump in the path of the edit/test
cycle. And what if, after a gnarly bug-squishing marathon, I forget
to remove a (break) from the source in some little-used code path and
accidentally check it into CVS? (And knowing me, this is absolutely
guaranteed to happen.)

[scissors of brevity]

> BTW, while may seem primitave to some mouse-addicted folks,
> compared
> IDE's that let you click on a line to insert a break point, having the
> debugger built into the languge is actually quite powerful--you could
> just as well write:
>
> (defun my-func (args)
> (when (some-condition-i-care-about args)
> (break "Entering my func with args: ~a" args))
> ...)

My first reaction to that is, "Cool!" I've often wanted such
functionality. But then my second reaction is that of course, in any
other language with an external debugger I could get it in exactly the
same way:

int foo(int* args) {
if (fsckd(args)) {
cout << "Set a breakpoint here";
}
...
}

def foo(*args):
if fsckd(*args):
print "Set a breakpoint here"
...

foo(Args) :-
(fsckd(Args),
write("Set a breakpoint here")
; true),
... .

: foo fsckd if
." Set a breakpoint here"
then ... ;

So in CL, doing the common thing is a relative pain, and doing the
flexible-but-rarely-needed thing is no easier than it would be in any
other environment. Am I missing something here?

> > Please forgive me if this is a FAQ. A pointer to relevant online
> > documentation would be more than sufficient.
>
> So I haven't yet written about debugging in Lisp but you might find
> some useful stuff in the draft chapters of the book I'm working on.
> They're available at:
>
> <http://www.gigamonkeys.com/book/>

I have begun reading your book on-line, and it looks quite good so
far. I'll buy a copy if I like the first two-three chapters :-)

Thanks for your response,

Joe Knapka

unread,
Sep 6, 2004, 10:57:59 AM9/6/04
to
niko...@random-state.net writes:

> Joe Knapka <jkn...@kneuro.net> wrote:
>
> > Common Lisp", I haven't been able to find any way to set a breakpoint,
> > in any of the CLs I've installed. (trace) is nice, but I'd like a way
> > to say, "please stop any time my-func is evaluated," and then end up
>
> The following should work in SBCL and CMUCL:
>
> (trace my-fun :break t)
>
> For extra goodness
>
> (trace my-fun :break t :wherein 'other-fun)
>
> will break only if the call to my-fun occurs within the dynamic scope
> of a call to other-fun.

Thank you! (I wish I'd seen this article before I replied to Peter.)

> CMUCL should allow you to start stepping from the debugger, SBCL doesn't
> yet, but if all goes to plan a new stepper that will allow this will be
> merged this month.

I am in a maze of twisty CL implementations, all ever-so-slightly
different...

Friedrich Dominicus

unread,
Sep 6, 2004, 1:10:24 PM9/6/04
to
Joe Knapka <jkn...@kneuro.net> writes:

> My first reaction to this is, "You mean I have to edit my source and
> recompile to set a simple breakpoint?!?"

Well that is a portable way, however there exists non-portable ways
which allow you to set a breakpoint on a function you like.

>
> [scissors of brevity]
>
>> BTW, while may seem primitave to some mouse-addicted folks,
>> compared
>> IDE's that let you click on a line to insert a break point, having the
>> debugger built into the languge is actually quite powerful--you could
>> just as well write:
>>
>> (defun my-func (args)
>> (when (some-condition-i-care-about args)
>> (break "Entering my func with args: ~a" args))
>> ...)
>
> My first reaction to that is, "Cool!" I've often wanted such
> functionality. But then my second reaction is that of course, in any
> other language with an external debugger I could get it in exactly the
> same way:
>
> int foo(int* args) {
> if (fsckd(args)) {
> cout << "Set a breakpoint here";
> }
> ...
> }

No, that is exactly not enough, because break stops the execution of
the program and does not just print something.

Regards
Friedrich

--
Please remove just-for-news- to reply via e-mail.

Message has been deleted

Pascal Costanza

unread,
Sep 6, 2004, 11:17:01 AM9/6/04
to
Joe Knapka wrote:

> My first reaction to this is, "You mean I have to edit my source and
> recompile to set a simple breakpoint?!?"

[...]

> So in CL, doing the common thing is a relative pain, and doing the
> flexible-but-rarely-needed thing is no easier than it would be in any
> other environment. Am I missing something here?

Yes, you are experiencing a culture clash. ;)

What you describe (setting breakpoints without changing the source code)
is usually a matter of using the right tools, not the programming
language's business. However, since Common Lisp does define a BREAK
operator people misunderstood your original question and expected that
you want an answer from the language perspective, not from the tool
perspective. (Common Lispers are generally used to expressing more
things within the language than outside of the language, because that's
one of Lisp's definitive strengths and gets extremely handy once you
know how to make use of it.)

Of course, setting breakpoints without changing the source code is also
possible for Common Lisp, but this depends on the actual implementation
that you use. I know that this feature exists for LispWorks, but I
rarely use it. I don't know enough about implementations, again because
I rarely use such breakpoints.

Pascal

--
Pascal Costanza University of Bonn
mailto:cost...@web.de Institute of Computer Science III
http://www.pascalcostanza.de Römerstr. 164, D-53117 Bonn (Germany)

Tayssir John Gabbour

unread,
Sep 6, 2004, 11:57:05 AM9/6/04
to
Joe Knapka wrote:

> Peter Seibel <pe...@javamonkey.com> writes:
> > Try the BREAK function. As in, change my-func to this:
> >
> > (defun my-func (args)
> > (break "Entering my func with args: ~a" args)
> > ...)

>
> My first reaction to this is, "You mean I have to edit my source and
> recompile to set a simple breakpoint?!?" I am, perhaps, spoiled, but
> sheesh, even gdb permits me to non-intrusively set a breakpoint. And
> if I hit a breakpoint, notice some fsck'd data further up the call
> chain, decide I need to break somewhere in the fsck'd frame and
> restart the test case... I have to recompile my code to do that?
> That seems like a serious speed-bump in the path of the edit/test
> cycle. And what if, after a gnarly bug-squishing marathon, I forget
> to remove a (break) from the source in some little-used code path and
> accidentally check it into CVS? (And knowing me, this is absolutely
> guaranteed to happen.)

Um... guys? I could be saying something particularly embarrassing
here, but isn't this lisp and there's a way to do this?

Now I haven't been coding for days, so maybe a neuron isn't hooked up
right, therefore feel free to cluestick me:


(defun breaker (fn name)
"Return a version of fn which calls a break before executing fn."
(setf (get name 'broken-function) fn)
(lambda (&rest args)
(break "Entering ~A with args: ~A" name args)
(apply fn args)))

(defun unbreak (fn-name)
"Revert the function to what it was before a break was added."
(let ((broken-function (get fn-name 'broken-function)))
(when broken-function
(setf (symbol-function fn-name) broken-function)
(setf (get fn-name 'broken-function) nil))))

(defun breakize (fn-name)
"Replace fn-name's global definition with a version which calls a
break beforehand."
(unbreak fn-name)
(setf (symbol-function fn-name)
(breaker (symbol-function fn-name)
fn-name)))


;; Example
(defun a (x y) (b x y))
(defun b (x y) (+ x y))

(breakize 'b)

(a 1 2) ;this should break, then if continued, return 3


(unbreak 'b)


MfG,
Tayssir

--
Observations of a circus which travelled to Iraq:
http://www.circus2iraq.org/updates.asp

Jens Axel Søgaard

unread,
Sep 6, 2004, 12:12:31 PM9/6/04
to
Joe Knapka wrote:
> Peter Seibel <pe...@javamonkey.com> writes:
>>Joe Knapka <jkn...@kneuro.net> writes:

>>[How do I set a breakpoint?]
>
>
>>Try the BREAK function. As in, change my-func to this:
>>
>> (defun my-func (args)
>> (break "Entering my func with args: ~a" args)
>> ...)
>
>
> My first reaction to this is, "You mean I have to edit my source and
> recompile to set a simple breakpoint?!?"

Edit the source - yes. Recompile - nope.

Just enter the new function definition in the repl,
and start the program without recompiling.

--
Jens Axel Søgaard

Vassil Nikolov

unread,
Sep 6, 2004, 1:05:50 PM9/6/04
to
tayss...@yahoo.com (Tayssir John Gabbour) writes:

> [...]


> (defun breakize (fn-name)
> "Replace fn-name's global definition with a version which calls a
> break beforehand."
> (unbreak fn-name)
> (setf (symbol-function fn-name)
> (breaker (symbol-function fn-name)
> fn-name)))

> [...]


Advise?


---Vassil.

--
Vassil Nikolov <vnik...@poboxes.com>

Hollerith's Law of Docstrings: Everything can be summarized in 72 bytes.

Fred Gilham

unread,
Sep 6, 2004, 1:26:51 PM9/6/04
to

> My first reaction to this is, "You mean I have to edit my source and
> recompile to set a simple breakpoint?!?" I am, perhaps, spoiled,
> but sheesh, even gdb permits me to non-intrusively set a breakpoint.
> And if I hit a breakpoint, notice some fsck'd data further up the
> call chain, decide I need to break somewhere in the fsck'd frame and
> restart the test case... I have to recompile my code to do that?

Ah, but GDB is not part of, say, the "C" language. It's an external
tool.

So for example in CMU Lisp, you can enter the debugger by typing
(break) at the REPL. Then you see the following:

* (break)

Break
[Condition of type SIMPLE-CONDITION]

Restarts:
0: [CONTINUE] Return from BREAK.
1: [ABORT ] Return to Top-Level.

Debug (type H for help)

(INTERACTIVE-EVAL (BREAK))
0] h

The prompt is right square brackets, the number indicating how many
recursive command loops you are in.
Debug commands do not affect * and friends, but evaluation in the
debug loop
do affect these variables.
Any command may be uniquely abbreviated.

Getting in and out of DEBUG:
Q throws to top level.
GO calls CONTINUE which tries to proceed with the restart
'continue.
RESTART invokes restart numbered as shown (prompt if not given).
ERROR prints the error condition and restart cases.
FLUSH toggles *flush-debug-errors*, which is initially t.

The name of any restart, or its number, is a valid command, and is
the same
as using RESTART to invoke that restart.

Changing frames:
U up frame D down frame T top frame B bottom
frame

[RETURN FOR MORE, Q TO QUIT HELP TEXT]:
F n goes to frame n.

Inspecting frames:
BACKTRACE [n] shows n frames going down the stack.
L lists locals in current function.
P, PP displays current function call.
SOURCE [n] displays frame's source form with n levels of
enclosing forms.
VSOURCE [n] displays frame's source form without any ellipsis.

Breakpoints and steps:
LIST-LOCATIONS [{function | :c}] list the locations for
breakpoints.
Specify :c for the current frame. Abbreviation: LL
LIST-BREAKPOINTS list the active breakpoints.
Abbreviations: LB, LBP
DELETE-BREAKPOINT [n] remove breakpoint n or all
breakpoints.
Abbreviations: DEL, DBP
BREAKPOINT {n | :end | :start} [:break form] [:function function]
[{:print form}*] [:condition form] set a breakpoint.
Abbreviations: BR, BP
STEP [n] step to the next location or step
n times.
[RETURN FOR MORE, Q TO QUIT HELP TEXT]:

Actions on frames:
DEBUG-RETURN expression
returns expression's values from the current frame, exiting the
debugger.
Abbreviations: R

See the CMU Common Lisp User's Manual for more information.

0]


You see that there is breakpoint functionality in the CMUCL debugger.
Most Lisps seem to have something like this.

Acutally I seldom use that functionality because when developing I use
an IDE such as SLIME that allows extremely convenient function-level
recompilation. So I can stick things in the function I'm debugging,
type "^C^C" and the function is redefined. This makes adding print
statements or even break statements and recompiling much less
intrusive than it would seem.

What's really magic is the way an IDE like SLIME can make it seem like
you can redefine functions while the program is still running. I've
done this more than once. I'm running my program from SLIME. I see
it's misbehaving, and realize what the problem is. I fix the source,
hit "^C^C" with the cursor in the source of the function I changed ---
without stopping the program --- and suddenly the program is doing the
right thing.

--
Fred Gilham gil...@csl.sri.com
The words of Judge Alex Kozinski, written in 1991, are not very
encouraging. "What we have learned from the experience of Eastern
Europe and the Soviet Union ... is that you need capitalism to make
socialism work." In other words, capitalism must produce what
socialism is to distribute. -- Janice Rogers Brown

Mark McConnell

unread,
Sep 6, 2004, 1:29:59 PM9/6/04
to
Joe Knapka <jkn...@kneuro.net> wrote in message news:<m3brgkyv...@localhost.localdomain>...
> Hi everyone,

>
> I haven't been able to find any way to set a breakpoint,
> in any of the CLs I've installed. (trace) is nice, but I'd like a way
> to say, "please stop any time my-func is evaluated," and then end up
> at a prompt where I can see what's about to be evaluated

[snip]

The posters who wrote about (break) or (break ...) were right on.
Once you're in the debugger, type :help or :? to find out what you can
do. Some typical things to try, from my mish-mash memory of various
Lisps:
:bt for backtrace
:up, :down or :dn, :down 2 to go down 2 stack frames
:loc for the local variables in the current frame
:q or :quit

If you used a variable that you forgot to define, the debugger should
let you substitute a value for it, either just that one time or by
setting the variable permanently. Sometimes you can even redefine or
recompile functions in the debugger, then continue using the new
version from the same point in the debugger as if nothing had
happened.

Also try (step [my-lisp-expression]) typed directly into the
interpreter.

Coby Beck

unread,
Sep 6, 2004, 1:44:07 PM9/6/04
to
"Joe Knapka" <jkn...@kneuro.net> wrote in message
news:m3pt4zx...@localhost.localdomain...

> Peter Seibel <pe...@javamonkey.com> writes:
> > Joe Knapka <jkn...@kneuro.net> writes:
>
> > [How do I set a breakpoint?]
>
> > Try the BREAK function. As in, change my-func to this:
> >
> > (defun my-func (args)
> > (break "Entering my func with args: ~a" args)
> > ...)
>
> My first reaction to this is, "You mean I have to edit my source and
> recompile to set a simple breakpoint?!?"

No. You can, but often you might just edit the code, *evaluate* the new
definition, undo the edit and run your test again. I used to do that
sometimes. Or I also used to copy the definition into another buffer or the
REPL and work on it there until I was satisfied and them put it back were it
belongs deleting the old definition. For some heavier and long lasting
sessions I often used to have a global *im-debugging* variable (sometimes a
whole set of them for different issues) and make sure breaks and prints and
debug-data caching things were always inside a (when *im-debugging* ...)
form. No harm for this to be left in shipped code.

Even better is a macro that will expand to nothing if expanded while your
debug var is nil but expand into debugging code otherwise.

> I am, perhaps, spoiled, but
> sheesh, even gdb permits me to non-intrusively set a breakpoint. And
> if I hit a breakpoint, notice some fsck'd data further up the call
> chain, decide I need to break somewhere in the fsck'd frame and
> restart the test case... I have to recompile my code to do that?

No. You can simply redefine some code, set some local variable values and
restart at a frame higher up. You never have to recompile for testing.

Allegro's and LispWorks IDE both let you set breakpoints without editing
code anyway.

> That seems like a serious speed-bump in the path of the edit/test
> cycle. And what if, after a gnarly bug-squishing marathon, I forget
> to remove a (break) from the source in some little-used code path and
> accidentally check it into CVS? (And knowing me, this is absolutely
> guaranteed to happen.)

This absolutely could happen if this is how you were debugging. I used to
debug that way alot.

> [scissors of brevity]
>
> > BTW, while may seem primitave to some mouse-addicted folks,
> > compared
> > IDE's that let you click on a line to insert a break point, having the
> > debugger built into the languge is actually quite powerful--you could
> > just as well write:
> >
> > (defun my-func (args)
> > (when (some-condition-i-care-about args)
> > (break "Entering my func with args: ~a" args))
> > ...)
>
> My first reaction to that is, "Cool!" I've often wanted such
> functionality. But then my second reaction is that of course, in any
> other language with an external debugger I could get it in exactly the
> same way:
>
> int foo(int* args) {
> if (fsckd(args)) {
> cout << "Set a breakpoint here";
> }
> ...
> }

[snip other similar examples]

There is actually a very substantial difference: BREAK puts you in the
Common Lisp debugger which is obviously more than a PRINT statement and
another world apart from debuggers you may be used to.

> So in CL, doing the common thing is a relative pain, and doing the
> flexible-but-rarely-needed thing is no easier than it would be in any
> other environment. Am I missing something here?

The only thing you are missing is that the common things in CL are not the
same as in other languages and (see just above) the
flexible-but-rarely-needed thing you are referring to is not even possible
in most other languages.

In general, your problem comes mostly from trying to do things in an unusual
and less than ideal (for Common Lisp) way.

Use the top level prompt, use incremental evaluation, test your code in
small pieces, get your mind away from the
"edit-compile-run-test-read-logs-of-output-fix" way of working. Get into
the "edit-evaluate-fix" mode it is much better!

HTH!

Petter Gustad

unread,
Sep 6, 2004, 1:44:20 PM9/6/04
to
Fred Gilham <gil...@snapdragon.csl.sri.com> writes:

> What's really magic is the way an IDE like SLIME can make it seem like
> you can redefine functions while the program is still running. I've
> done this more than once. I'm running my program from SLIME. I see
> it's misbehaving, and realize what the problem is. I fix the source,
> hit "^C^C" with the cursor in the source of the function I changed ---
> without stopping the program --- and suddenly the program is doing the
> right thing.

In SLIME you have a neat debugger interface where you can move up and
down stack frames, inspect data, do evals in minibuffers, etc.

Petter
--
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

Peder O. Klingenberg

unread,
Sep 6, 2004, 3:19:52 PM9/6/04
to
Fred Gilham <gil...@snapdragon.csl.sri.com> writes:

>> My first reaction to this is, "You mean I have to edit my source and
>> recompile to set a simple breakpoint?!?" I am, perhaps, spoiled,
>> but sheesh, even gdb permits me to non-intrusively set a breakpoint.
>> And if I hit a breakpoint, notice some fsck'd data further up the
>> call chain, decide I need to break somewhere in the fsck'd frame and
>> restart the test case... I have to recompile my code to do that?
>
> Ah, but GDB is not part of, say, the "C" language. It's an external
> tool.

And one that is rarely used in production code. That's IMO one of the
great features of the CL debugger. It's always there, even in
delivered images (at least mine).

Just the other day, I (re-)discovered how immensely useful this
feature is. A process of mine in a production environment encountered
some data which it didn't expect. I hadn't been paranoid enough, and
had assumed our communication partner actually managed to follow their
own detailed protocol. A unique constraint in the database was broken
as a result.

My process in turn feeds data to a lot of other processes, and
unscheduled restarts are a hassle. But because lisp is such a
friendly language, instead of crashing and giving me a core dump, the
failing program landed me in a debugger I could attach to. Using
this, it was a breeze not only diagnosing the problem, but fixing it
without restarting the whole chain of processes.

After testing and checking in the fix, I pasted the new, paranoid
definition of my function into the debugger. The new function would
just return nil in these cases. Then I manually told the debugger to
return nil in the current case, and watched the wheels starting to
turn again.

GDB would possibly have helped in diagnosing the problem from the core
dump, if a core dump had been produced and if the problem hadn't been
obvious from the error messages prior to the crash.

If I had been extremely cautious, I could maybe have arranged for the
production process to be started under GDB every time, and so when the
crash occured, I would again have been in a debugger, and could
probably manually have returned NULL. I've never known production
processes that were run this way, but I suppose it's possible.

But fixing the running image? I have certainly never seen GDB used
that way. I haven't used GDB enough to say that it's impossible,
maybe if you're a C/assembly god, you could have done some magic by
calling dlopen from GDB or something, but give me a break (pun
intended).

> Acutally I seldom use that functionality because when developing I use
> an IDE such as SLIME that allows extremely convenient function-level
> recompilation. So I can stick things in the function I'm debugging,
> type "^C^C" and the function is redefined. This makes adding print
> statements or even break statements and recompiling much less
> intrusive than it would seem.

Ditto. I don't think I've ever used the lisp debugger to insert
breakpoints. For exactly the same reasons.

...Peder...
--
I wish a new life awaited _me_ in some off-world colony.

Pascal Bourguignon

unread,
Sep 6, 2004, 3:53:26 PM9/6/04
to
pe...@news.klingenberg.no (Peder O. Klingenberg) writes:
> If I had been extremely cautious, I could maybe have arranged for the
> production process to be started under GDB every time, and so when the
> crash occured, I would again have been in a debugger, and could
> probably manually have returned NULL. I've never known production
> processes that were run this way, but I suppose it's possible.
>
> But fixing the running image? I have certainly never seen GDB used
> that way. I haven't used GDB enough to say that it's impossible,
> maybe if you're a C/assembly god, you could have done some magic by
> calling dlopen from GDB or something, but give me a break (pun
> intended).

The only know instance where it's possible is with Apple's Xcode, but
only if you compile with the debugging option.

Martin Ginkel

unread,
Sep 6, 2004, 4:03:23 PM9/6/04
to
For debugging, there is no standard:
look into the doc of your implementation.
In ACL this is simply:

:trace ((fun :break-before t))

Without redefining a lot of code...

Martin

Matthew Danish

unread,
Sep 6, 2004, 4:18:35 PM9/6/04
to
Joe Knapka <jkn...@kneuro.net> writes:
> My first reaction to this is, "You mean I have to edit my source and
> recompile to set a simple breakpoint?!?" I am, perhaps, spoiled, but
> sheesh, even gdb permits me to non-intrusively set a breakpoint. And
> if I hit a breakpoint, notice some fsck'd data further up the call
> chain, decide I need to break somewhere in the fsck'd frame and
> restart the test case... I have to recompile my code to do that?
> That seems like a serious speed-bump in the path of the edit/test
> cycle. And what if, after a gnarly bug-squishing marathon, I forget
> to remove a (break) from the source in some little-used code path and
> accidentally check it into CVS? (And knowing me, this is absolutely
> guaranteed to happen.)

Well, the first and obvious point is that GDB is not part of the ANSI
C language; and in the same manner, CL debuggers are permitted to go
above and beyond the capabilities described in the standard. Most
people do get by with the plain old BREAK function, and this is not so
bad in CL because of the almost instant feedback you get from being
able to evaluate any expression into a running image, unlike C where
you have to recompile/rerun/debug.

From a broader perspective, however, you can see that BREAK is merely
a simple wrapper around the more versatile CERROR function; which
itself is a simplified interface to the restart and condition system.
The Lisp debugger is in fact merely a user-interface to this system; a
system which provides a very powerful protocol for indicating and
resolving exceptional states in a program. You probably will not
begin to make use of this system in an advanced way for quite a while,
but it is good to know about.

Kent Pitman has written a paper on the condition system:
http://www.nhplace.com/kent/Papers/Condition-Handling-2001.html

Peter Seibel has a chapter in his book about it:
http://www.gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html

--
;;;; Matthew Danish -- user: mrd domain: cmu.edu
;;;; OpenPGP public key: C24B6010 on keyring.debian.org

Coby Beck

unread,
Sep 6, 2004, 5:12:14 PM9/6/04
to

"Martin Ginkel" <martin...@epost.de> wrote in message
news:chig08$kej$1...@news.boerde.de...

> For debugging, there is no standard:

What is all this about then! ;)

HyperSpec: 25.1.2 Debugging Utilities
The next figure shows defined names relating to debugging.


*debugger-hook* documentation step
apropos dribble time
apropos-list ed trace
break inspect untrace
describe invoke-debugger
Figure 25-2. Defined names relating to debugging

This is the base minimum, any decent implementation will provide more stuff.

Jon Boone

unread,
Sep 6, 2004, 5:52:55 PM9/6/04
to
On 2004-09-06 15:53, in article 87isard...@thalassa.informatimago.com,
"Pascal Bourguignon" <sp...@mouse-potato.com> wrote:

> pe...@news.klingenberg.no (Peder O. Klingenberg) writes:
>> If I had been extremely cautious, I could maybe have arranged for the
>> production process to be started under GDB every time, and so when the
>> crash occured, I would again have been in a debugger, and could
>> probably manually have returned NULL. I've never known production
>> processes that were run this way, but I suppose it's possible.
>>
>> But fixing the running image? I have certainly never seen GDB used
>> that way. I haven't used GDB enough to say that it's impossible,
>> maybe if you're a C/assembly god, you could have done some magic by
>> calling dlopen from GDB or something, but give me a break (pun
>> intended).
>
> The only know instance where it's possible is with Apple's Xcode, but
> only if you compile with the debugging option.

There was a debugger that I used back in the early 1990s on Unix systems
at CMU that not only had an X-based GUI interface which allowed breakpoint
insertion via point-and-click, but it also allowed you to modify values and
even insert new code, without recompiling. I can't remember what it's
called, but I've always missed that functionality.

--jon

Frode Vatvedt Fjeld

unread,
Sep 6, 2004, 5:53:18 PM9/6/04
to
Matthew Danish <mrd+n...@cmu.edu> writes:

> From a broader perspective, however, you can see that BREAK is
> merely a simple wrapper around the more versatile CERROR function;
> which itself is a simplified interface to the restart and condition
> system.

I believe break should not be considered an interface to the condition
syste, and that it cannot possibly be implemented as a wrapper around
cerror. Rather, it's a wrapper around invoke-debuger, which is
something quite different.

As the CLHS says:

break could be defined by:

(defun break (&optional (format-control "Break") &rest format-arguments)
(with-simple-restart (continue "Return from BREAK.")
(let ((*debugger-hook* nil))
(invoke-debugger
(make-condition 'simple-condition
:format-control format-control
:format-arguments format-arguments))))
nil)

--
Frode Vatvedt Fjeld

Carl Shapiro

unread,
Sep 6, 2004, 9:01:07 PM9/6/04
to
pe...@news.klingenberg.no (Peder O. Klingenberg) writes:

> If I had been extremely cautious, I could maybe have arranged for the
> production process to be started under GDB every time, and so when the
> crash occured, I would again have been in a debugger, and could
> probably manually have returned NULL. I've never known production
> processes that were run this way, but I suppose it's possible.
>
> But fixing the running image? I have certainly never seen GDB used
> that way. I haven't used GDB enough to say that it's impossible,
> maybe if you're a C/assembly god, you could have done some magic by
> calling dlopen from GDB or something, but give me a break (pun
> intended).

If you are running your object code on a system that actually has an
operating-system supported debugging facility (such as Windows) you
are given the option at fault time to attach a debugger to the running
process. From within the debugger you can monkey around with the
suspended process, single step for a while, evaluate function
definitions, detach the debugger, and resume process execution.

Since the Cretaceous Period of computing, systems have had the ability
to link symbolic debuggers into applications to handle faults as Lisp
debuggers do today. UNIX, a notable exception, was a huge step
backwards for process debuggability. I hate to see Lisp systems
compared to monstrously programmer-unfriendly artifacts such as UNIX
and GDB.

Icarus Sparry

unread,
Sep 6, 2004, 9:40:05 PM9/6/04
to
On Mon, 06 Sep 2004 17:52:55 -0400, Jon Boone wrote:
> There was a debugger that I used back in the early 1990s on Unix systems
> at CMU that not only had an X-based GUI interface which allowed breakpoint
> insertion via point-and-click, but it also allowed you to modify values and
> even insert new code, without recompiling. I can't remember what it's
> called, but I've always missed that functionality.

Maybe 'ups', now hosted at ups.sourceforge.net, but originally written at
the University of Kent in the UK. Includes a 'C' interpreter which allows
you to patch compiled code.

Jon Boone

unread,
Sep 6, 2004, 9:51:26 PM9/6/04
to
On 2004-09-06 21:40, in article
pan.2004.09.07....@icarus.freeuk.com, "Icarus Sparry"
<use...@icarus.freeuk.com> wrote:

Yes, that's it. Quite nice.

--jon

Greg Menke

unread,
Sep 6, 2004, 10:02:53 PM9/6/04
to
Joe Knapka <jkn...@kneuro.net> writes:


> Hi Peter,
>
> Peter Seibel <pe...@javamonkey.com> writes:
>
> My first reaction to this is, "You mean I have to edit my source and
> recompile to set a simple breakpoint?!?" I am, perhaps, spoiled, but
> sheesh, even gdb permits me to non-intrusively set a breakpoint.

Its not quite that simple. Depending on the archtecture involved, gdb
may have to resort to hand-compiling hardware break instructions into
the selected addresses, taking the exeception, then re-introducing the
old instructions so the they can be executed on return from the
debugger. In the cases where hardcoded break instructions aren't
required and there is hardware support for a breakpoint, gdb still has
to manage the breakpoints and single-steps- so its by no means
non-intrusive and to debug usefully. In general you need to compile
with no optimization anyhow- so the very act of introducing the
debugger will perturb the runtime structure of the program you're
trying to debug.

It only looks non-intrusive because gdb is taking advantage of the
nice simple execution environment. That said, sometimes I also miss a
nice step-into, step-over interface when debugging Lisp code. Though
too be fair, those are often the same times I'm trying to debuging a
complicated, over-long function...

Gregm

Joel Ray Holveck

unread,
Sep 7, 2004, 1:45:31 AM9/7/04
to
> My first reaction to this is, "You mean I have to edit my source and
> recompile to set a simple breakpoint?!?"
[snip]

> restart the test case... I have to recompile my code to do that?
> That seems like a serious speed-bump in the path of the edit/test
> cycle.

Most people have told you about the difference between BREAK as a
feature of Common Lisp in general, as opposed to setting breakpoints
using your Lisp system. So I'm not going to go into detail on that
one. Instead, I'd like to talk about something that makes Lisp great:
incremental refinement.

The idea of restarting a Lisp program to test a fix or find a bug is
as alien to most Lisp programmers as restarting Apache to test a
changed CGI script would be web developers. It's just a different
development cycle.

In the programming languages most people are used to, there's a cycle
something like this:
1. Edit the source code
2. Compile it
3. Run a test case
4. Note the most obvious bug
5. Run the debugger
6. Set a breakpoint in the region of the bug
7. Examine the program's state around the breakpoint
8. Think of a solution
9. Return to step 1

Why does this cycle occur? Because in most languages, a program must
be complete before it can be compiled, and compiled before it can be
run, and must run subroutines in the natural order of running. This
cycle is pretty much mandated by these prerequisites.

This is reflected in the tool structure: the editor, compiler,
execution shell, and debugger are all separate programs. In Lisp,
things are different.

In Lisp, you're sitting "within" the running program at all times.
The execution shell is the Lisp REPL (that is, the Lisp prompt). The
debugger is always present, and takes control whenever there's an
unhandled exception. The compiler is just part of your execution
shell. And the shell has access to all of your subroutines. The only
thing that's not necessarily integrated is the editor, and most Lisp
coders use editors that integrate themselves with Lisp; usually, this
is ilisp or SLIME under Emacs, or an editor built into the vendor's
Lisp.

In a very real sense, a Lisp programmer has the internals of his
program as well as all his Lisp tools available to him all the time.
This is very different than other systems, in which the tools are
separate from each other and, in many senses, from your program.

Let's suppose you had the following simple program to test numbers
that a user enters, and tell whether they're divisible by 10. It has
an obvious bug. (This also isn't the best way to implement this, but
it's just a demonstration.)

(defun divisible-by-10-p (number)
(zerop (mod number 11)))
(defun list-multiples-of-10 ()
(loop
for number = (progn (print "Enter a number, or NIL to exit:") (read))
while number
when (divisible-by-10-p number)
do (print "It's divisible by 10!")))

Now, as I said, this has an obvious bug: it lists numbers that are
divisible by 11 instead of 10.

Let's suppose you've loaded this into your Lisp. When you run it, the
code prints some numbers that aren't right. You want to set a
breakpoint. You don't need to restart your test case; you don't even
need to stop your main loop. Just leave it running as it is.
Remember, in Lisp, you're inside the development system, rather than
outside of it.

Now, in your editor, you go exactly where you want the breakpoint to
be. Add a (break):
(defun divisible-by-10-p (number)
(break)
(zerop (mod number 11)))
In most Lisp editors (including the ones I mentioned above), you can
give it an "evaluate defun" command of some sort. In mine, I press
Control-C twice while I'm in the defun. Again, in most Lisp editors,
this will happen while you're still in the main loop. You'll still be
in the main loop, right where you left off.

Here, I used the example of adding a break, although as many people
mentioned, most Lisp environments have the ability to turn on a
break-on-entry without you needing to modify your code. But to fix
the bug, you will need to edit the code, and the same thing applies
there.

You can see how this would scale very well. For example, the main
loop could be a large program's GUI event handler. You may have gone
through several steps to get the bug's reproduction scenario set up.
Being able to modify your code and test it while your program is still
running may be very, very valuable. It's valuable in production
programs too: at my workplace, I use the same idea to load patches
into a running server without needing to take 30 minutes to repopulate
its data. But I'm jumping ahead; let's stay on the focus of debugging.

Let's suppose you don't have a Lisp editor. Maybe you haven't learned
Emacs or your vendor's editor yet, and are using vi to edit your code
and running your Lisp from a command prompt. That's okay too. You
can press a break key (usually Control-C) while your program is
running, and you'll get a debugger prompt. From there, you can either
load your modified source file, or if the new function is short, just
type it in by hand. Then tell the debugger to continue where it left
off.

If you want, then at the debugger prompt, you can call your inner
function by itself, such as (divisible-by-10-p 10). You can look at
what it does, and call it again and again as you refine it. If you
need variables, such as complex data structures, that your program
maintains while it's running, they're all available. All your
subroutines are available. In this respect, it's similar to gdb's
"call" and "print" commands, with one important difference: you can
edit your function while you're testing.

Eventually, either because you think you've got things working or
because you don't feel like testing your function in the debugger, you
tell the debugger to continue. Your program picks up right where it
left off, and you can try the errant command again (in this case,
submit test numbers like "10" and "11" back to the loop's prompt).

Remember, this entire debugging session-- editing, tracing, debugging,
correcting, testing-- took place while your program was still running.
You don't need separate tools for these steps that run outside of your
program; they're integrated with the environment your program is
running in. And once you've tested your addition, you can compile it
with (compile 'divisible-by-10-p) if you want to. (Most editors have
a command to load and compile a modified defun in the same command.)
Or just leave it interpreted while you go debug another part of your
program, still within the same session.

In real life, I rarely use the debugger like this, though. Usually,
I'll test an inner function directly from the REPL. If it fails, I
hypothesize about the problematic function and call it directly, or
possibly turn on some traces instead. Since you have all of your
program's subroutines and variables at the Lisp REPL, you can do
one-shot tests from the prompt, without having to write and compile
tests. In C, a test often has to build a data structure artificially,
then a function call to the function in question, then have a function
to print the resulting data structure. This means that people rarely
test small things when writing in C.

In Lisp, though, things are different. Since your entire program is
available, you can have it load up its data in the normal way-- by
interacting with the user, or reading its data files, or whatever.
Then you can call small functions individually, editing their
definitions as you go, and check the results. You don't need to write
specialized printers for your test cases, though: Lisp has a built-in
customizable printer for most data structures, DESCRIBE and EXAMINE
can get you through the rest.

Once you've got your program's data loaded, you can go wild with
development. It's not uncommon for me to have a session that lasts a
week, debugging existing code and writing new code, all without ever
restarting my program.

There's a lot of things available to make debugging easy in Lisp that
aren't so easy in other languages. But the key concept, and this is
one that takes time to get used to, is that the development cycle is
different. When you're at the REPL, you're immersed in both your
program and all of Lisp's tools. In a very real sense, you're within
your program. If you think in these terms-- and it's hard at first,
after years of ALGOL-descended conditioning-- then Lisp debugging will
make a lot more sense.

joelh

Peder O. Klingenberg

unread,
Sep 7, 2004, 8:07:15 AM9/7/04
to
Carl Shapiro <cshapi...@panix.com> writes:

> If you are running your object code on a system that actually has an
> operating-system supported debugging facility (such as Windows) you
> are given the option at fault time to attach a debugger to the running
> process.

You can redefine functions from the debugger in Windows? Cool. I
admit I don't know much about Windows. (nor many other non-unixish
OSes. I have a Symbolics machine with a ton of documentation sitting
around that I want to play with, but I seem to have a shortage of
round tuits.)

> I hate to see Lisp systems compared to monstrously
> programmer-unfriendly artifacts such as UNIX and GDB.

But the comparisons generally come out in favor of Lisp. :)

Really, my post was intended more to convey how happy I am with the
debugger in Lisp than how unhappy and/or ignorant I am with any other
environment. GDB was mentioned in the parent post, so I just took it
from there.

The point is, I like the fact that the Lisp debugger is there by
default and powerful enough to let me rescue my processes with
relative ease. Regardless of the capabilities of "competing"
environments, and despite the fact that the CL spec does not specify
how to set breakpoints without editing code.

Peter Seibel

unread,
Sep 7, 2004, 10:45:06 AM9/7/04
to
peter....@swipnet.se (Peter Lewerin) writes:

> Peter Seibel <pe...@javamonkey.com> wrote
>
>> Lisp is the red pill. -- John Fraser, comp.lang.lisp
>
> You know, the other day I was eating a juicy and delicious steak, and
> it made me think about your .sig. It's getting close to twenty years
> since I first used Lisp, and some eleven of those years I've used
> ALGOL-style languages. After nine years, you know what I realize?
>
> Lisp isn't always doing what the Man tells you, fighting unkillable
> bugs, being tired and cold, eating the same goddamn goop everyday...
>
> Lisp is bliss. No more trying to make sense of C or Perl alphabet
> soup: all I see now is blonde, brunette, redhead.
>
> Lisp isn't the red pill. It's the blue pill.
>
> Lisp. What a mind-job.

;-)

-Peter

--
Peter Seibel pe...@javamonkey.com

Lisp is the red pill. -- John Fraser, comp.lang.lisp

Fred Gilham

unread,
Sep 7, 2004, 10:46:43 AM9/7/04
to

Here's a session using SLIME in the spirit of Joel's example:

I took the following code and ran it under SLIME:

(defun divisible-by-10-p (number)
(zerop (mod number 11)))
(defun list-multiples-of-10 ()

(do ((number 0 (1+ number)))
((> number 100000))
(when (divisible-by-10-p number)
(format t "~A is divisible by 10.~%" number)
(force-output)
(sleep 1))))


In the slime-repl window I got the following output:

CL-USER> (list-multiples-of-10)
0 is divisible by 10.
11 is divisible by 10.
22 is divisible by 10.
33 is divisible by 10.
44 is divisible by 10.
55 is divisible by 10.
66 is divisible by 10.
77 is divisible by 10.
88 is divisible by 10.
99 is divisible by 10.


While this was happening "it hit me" that the program was doing the
wrong thing. So, without stopping the program, in the source window I
made the following change:

(defun divisible-by-10-p (number)
(zerop (mod number 10)))


Then, in the source window I hit ^C^C while the cursor was in the
DIVISIBLE-BY-10-P defun.

The output in the repl window, which was continuing while I did this,
gave the following:

; Converted DIVISIBLE-BY-10-P.
; Compiling DEFUN DIVISIBLE-BY-10-P:

; In: DEFUN DIVISIBLE-BY-10-P

; (MOD NUMBER 10)
; --> BLOCK LET IF AND IF AND IF PLUSP >
; ==>
; NUMBER
; Note: Deleting unreachable code.
;
; Byte Compiling Top-Level Form:

; Compilation unit finished.
; 1 note

100 is divisible by 10.
110 is divisible by 10.
120 is divisible by 10.
130 is divisible by 10.
140 is divisible by 10.
150 is divisible by 10.
160 is divisible by 10.
170 is divisible by 10.


This all happened without entering the debugger at all --- at least
visibly.

--
Fred Gilham gil...@csl.sri.com
Comprehensive Computer Language Preference Survey
Do you like Lisp? (Check one)
[ ] Yes [ ] Sure [ ] You bet [ ] Yep [ ] Da

Joel Ray Holveck

unread,
Sep 7, 2004, 3:10:40 PM9/7/04
to
> Here's a session using SLIME in the spirit of Joel's example:

After I wrote that message, I decided to check out this SLIME thing.
I'd been stuck in my ilisp ways for years now. I'd tried out SLIME a
while ago (it seems like years, but has it been around that long?) and
couldn't use it for some reason, but since they released a 1.0 this
month I thought I'd check it out.

Wow! Boy, was I blown away! I mean to tell you, this is a seriously
cool interface! The first time the debugger popped up, I started to
go to the REPL window to enter a restart like I usually do under
ilisp. Then I thought, "Hey, that's not the normal error formatting.
Waitasec..." I moved my mouse over the restart I wanted, middle
clicked, at sat there looking at the screen saying "wooooah".
Finally, a Lisp debugger in Emacs! If only we had source-level
stepping like edebug, but I'm sure somebody's got that in mind.

I gotta say, SLIME is something else! CL scratch buffers, C-c :,
multiple connections, an icomplete-style M-Tab, a built-in inspector,
marking your source with compiler warnings, there's just a lot of Wow
to make my coding life easier.

This is seriously cool.

Cheers,
joelh

Joe Knapka

unread,
Sep 7, 2004, 4:13:43 PM9/7/04
to
pe...@news.klingenberg.no (Peder O. Klingenberg) writes:

> Carl Shapiro <cshapi...@panix.com> writes:
>
> > If you are running your object code on a system that actually has an
> > operating-system supported debugging facility (such as Windows) you
> > are given the option at fault time to attach a debugger to the running
> > process.
>
> You can redefine functions from the debugger in Windows? Cool.

Maybe. It doesn't always work (you might get a "Sorry, I couldn't
relink this code because the current call stack passes through it").
Anyway, by the time you've got a segmentation fault on your hands
it's usually too late, since any attempt to restart the code only
signals the same error again.

> --
> I wish a new life awaited _me_ in some off-world colony.

Ditto. (The actualization of your sig would render mine irrelevant :-)

-- Joe

--
"We sat and watched as this whole <-- (Died Pretty -- "Springenfall")
blue sky turned to black..."
... Re-defeat Bush in '04.
--
pub 1024D/BA496D2B 2004-05-14 Joseph A Knapka
Key fingerprint = 3BA2 FE72 3CBA D4C2 21E4 C9B4 3230 94D7 BA49 6D2B
If you really want to get my attention, send mail to
jknapka .at. kneuro .dot. net.

Joe Knapka

unread,
Sep 7, 2004, 4:33:16 PM9/7/04
to
"Coby Beck" <cb...@mercury.bc.ca> writes:

> "Joe Knapka" <jkn...@kneuro.net> wrote in message
> news:m3pt4zx...@localhost.localdomain...
> > Peter Seibel <pe...@javamonkey.com> writes:
> > > Joe Knapka <jkn...@kneuro.net> writes:
> >
> > > [How do I set a breakpoint?]
> >
> > > Try the BREAK function. As in, change my-func to this:
> > >
> > > (defun my-func (args)
> > > (break "Entering my func with args: ~a" args)
> > > ...)
> >
> > My first reaction to this is, "You mean I have to edit my source and
> > recompile to set a simple breakpoint?!?"
>
> No. You can, but often you might just edit the code, *evaluate* the new
> definition, undo the edit and run your test again. I used to do that
> sometimes. Or I also used to copy the definition into another buffer or the
> REPL and work on it there until I was satisfied and them put it back were it
> belongs deleting the old definition. For some heavier and long lasting
> sessions I often used to have a global *im-debugging* variable (sometimes a
> whole set of them for different issues) and make sure breaks and prints and
> debug-data caching things were always inside a (when *im-debugging* ...)
> form. No harm for this to be left in shipped code.
>
> Even better is a macro that will expand to nothing if expanded while your
> debug var is nil but expand into debugging code otherwise.

OK, I'm with you so far, I think...

But of course, I could always set a breakpoint on the "set a breakpoint"
line, and thence inspect values, step code, etc. True, in most cases
I couldn't actually redefine code from the debug prompt, so that is a
big difference.

> > So in CL, doing the common thing is a relative pain, and doing the
> > flexible-but-rarely-needed thing is no easier than it would be in any
> > other environment. Am I missing something here?
>
> The only thing you are missing is that the common things in CL are not the
> same as in other languages and (see just above) the
> flexible-but-rarely-needed thing you are referring to is not even possible
> in most other languages.
>
> In general, your problem comes mostly from trying to do things in an unusual
> and less than ideal (for Common Lisp) way.
>
> Use the top level prompt, use incremental evaluation, test your code in
> small pieces, get your mind away from the
> "edit-compile-run-test-read-logs-of-output-fix" way of working. Get into
> the "edit-evaluate-fix" mode it is much better!

So let me be sure I understand this: when my code stops in the
debugger due to an error condition, I should be able to redefine the
function in which the error occurred, and restart execution at the
point of the failed call? Without having to restart my entire
program? That sounds really useful, but it's not obvious exactly how
to accomplish it at the debug prompt (and I assume this is also
implementation-specific, but it's not obvious in any of the CLs
I've tried so far).

I am, BTW, using SLIME, but I'd also like to understand how to do
these magic debug things at the (various) REPL(s).

Thanks,

Joe Knapka

unread,
Sep 7, 2004, 4:46:35 PM9/7/04
to
Greg Menke <gregm...@toadmail.com> writes:

> Joe Knapka <jkn...@kneuro.net> writes:
>
>
> > Hi Peter,
> >
> > Peter Seibel <pe...@javamonkey.com> writes:
> >
> > My first reaction to this is, "You mean I have to edit my source and
> > recompile to set a simple breakpoint?!?" I am, perhaps, spoiled, but
> > sheesh, even gdb permits me to non-intrusively set a breakpoint.
>
> Its not quite that simple.

True, but it certainly //looks// that simple from the developer's
perspective.

> Depending on the archtecture involved, gdb
> may have to resort to hand-compiling hardware break instructions into
> the selected addresses, taking the exeception, then re-introducing the
> old instructions so the they can be executed on return from the
> debugger. In the cases where hardcoded break instructions aren't
> required and there is hardware support for a breakpoint, gdb still has
> to manage the breakpoints and single-steps- so its by no means
> non-intrusive and to debug usefully. In general you need to compile
> with no optimization anyhow- so the very act of introducing the
> debugger will perturb the runtime structure of the program you're
> trying to debug.

Yes, and this fact has caused problems for me from time to time :-)
However, in the vast majority of cases code compiled with debugging
information does not perform noticably different than optimized code
for my purposes. No doubt there are domains, such as hard-realtime
work, where that wouldn't be the case.

Joe Knapka

unread,
Sep 7, 2004, 4:57:35 PM9/7/04
to
Wow. Thank you for this detailed explanation. You have described
some very impressive capabilities that I am going to explore
ASAP.

Thanks to everyone else in this thread, as well, for the very
educational dialogue.

-- Joe

Joel Ray Holveck <jo...@juniper.net> writes:

[A bunch of very cool stuff, which I have snipped]

jayessay

unread,
Sep 7, 2004, 5:27:11 PM9/7/04
to
Joe Knapka <jkn...@kneuro.net> writes:

> "Coby Beck" <cb...@mercury.bc.ca> writes:

> > Use the top level prompt, use incremental evaluation, test your code in
> > small pieces, get your mind away from the
> > "edit-compile-run-test-read-logs-of-output-fix" way of working. Get into
> > the "edit-evaluate-fix" mode it is much better!
>
> So let me be sure I understand this: when my code stops in the
> debugger due to an error condition, I should be able to redefine the
> function in which the error occurred, and restart execution at the
> point of the failed call? Without having to restart my entire
> program?

Absolutely. Works like magic.


> That sounds really useful, but it's not obvious exactly how to
> accomplish it at the debug prompt (and I assume this is also
> implementation-specific, but it's not obvious in any of the CLs I've
> tried so far).

You can get this implementation independently via Ilisp or Slime.


> I am, BTW, using SLIME, but I'd also like to understand how to do
> these magic debug things at the (various) REPL(s).

Check out the Slime doc. Actually you don't even need to look at the
REPL - the Slime facility will handle all that stuff for you as well
as poping up the results in your favorite way.


/Jon

--
'j' - a n t h o n y at romeo/charley/november com

Coby Beck

unread,
Sep 7, 2004, 6:07:46 PM9/7/04
to

"Joe Knapka" <jkn...@kneuro.net> wrote in message
news:m3zn41x...@localhost.localdomain...

> "Coby Beck" <cb...@mercury.bc.ca> writes:
> > Even better is a macro that will expand to nothing if expanded while
your
> > debug var is nil but expand into debugging code otherwise.
>
> OK, I'm with you so far, I think...

I mean something like this:

CL-USER 39 > (defvar *development-build* nil)
;; have this somewhere in your file loading
CL-USER 40 > (setf *development-build* t)
T ;; have this in some local file on your machine
CL-USER 41 > ;; define something like this
(defmacro debug-when (condition &optional (format-string "Break")
&rest format-args)
(when *development-build*
`(when ,condition
(break ,format-string ,@format-args))))
DEBUG-WHEN

;; put it in some trouble spot
CL-USER 42 > (defun foo (n)
(debug-when (= n 0)
"the time is: ~A" (get-universal-time))
(* n n))
FOO

CL-USER 43 > (foo 1)
1

CL-USER 44 > (foo 0)

the time is: 3303582168
1 (continue) Return from break.
2 (abort) Return to level 0.
3 Return to top loop level 0.

Type :b for backtrace, :c <option number> to proceed, or :? for other
options

CL-USER 45 : 1 > :a

;; make sure this is somewhere local to the build machine
CL-USER 46 > (setf *development-build* nil)
NIL

CL-USER 47 > (defun foo (n)
(debug-when (= n 0)
"the time is: ~A" (get-universal-time))
(* n n))
FOO

CL-USER 48 > (foo 0)
0
;; no break!
CL-USER 49 >

This can get more and more full featured until you one day realize you are
just reinventing the existing condition system!

> > Use the top level prompt, use incremental evaluation, test your code in
> > small pieces, get your mind away from the
> > "edit-compile-run-test-read-logs-of-output-fix" way of working. Get
into
> > the "edit-evaluate-fix" mode it is much better!
>
> So let me be sure I understand this: when my code stops in the
> debugger due to an error condition, I should be able to redefine the
> function in which the error occurred, and restart execution at the
> point of the failed call? Without having to restart my entire
> program? That sounds really useful, but it's not obvious exactly how

Yes it is, it can be great. I've even logged in remotely to a client's
running application, patched code (ie redefined methods etc.) and told them
"Okay, try that again"

Then put the code into a patch file so the next restart would have the
correct code and made sure the corrected code was now in the main repository
so the next delivery would not need the patch.

> to accomplish it at the debug prompt (and I assume this is also
> implementation-specific, but it's not obvious in any of the CLs
> I've tried so far).

The tools can really vary alot. I recall with ACL it was very easy. An
error put you in the graphical debugger, you could click stack frames,
inspect object state, return an object to the top level, play with it then
restart at whatever frame you chose. Or maybe look and say to yourself,
"hmm, I forgot that slot might not have been initialized..." Edit the code
to handle that situation and restart at that function call. This is a HUGE
win when you have really complex state that takes a long time to get to and
may ber very hard to reproduce exactly.

LW I am less adept with.

These thing are great and often hard to live without but I find almost
equally valuable is being able to test your code line by line as you build
it. I find I need the debugger less in the kind of development I do now, I
take more advantage or incremental development and MACROEXPAND.

Carl Shapiro

unread,
Sep 7, 2004, 7:08:05 PM9/7/04
to
Joe Knapka <jkn...@kneuro.net> writes:

> Maybe. It doesn't always work (you might get a "Sorry, I couldn't
> relink this code because the current call stack passes through it").
> Anyway, by the time you've got a segmentation fault on your hands
> it's usually too late, since any attempt to restart the code only
> signals the same error again.

First off, runtime patching of functions on the call stack is not a
problem. Second, you can almost always continue from access violation
exceptions. Naturally, if your program has overwhelmingly corrupted
itself there may be no reason to. That's life. For the record, I
routinely "edit and continue" as well restart from various types of
exceptions on Windows when working on the CMUCL runtime. (How's that
for introducing circularity to this discussion?) It just works.

Joe Knapka

unread,
Sep 7, 2004, 8:35:00 PM9/7/04
to
Carl Shapiro <cshapi...@panix.com> writes:

We're talking (in this tangent) about Visual Studio, right? I haven't
tried "edit & continue" under .NET yet, but I recall getting
"Sorry, couldn't edit and continue" errors sufficiently frequently
under VS6 that I quit trying to use it. Maybe I'm remembering something
else though; it's been quite a while since I did any C++ on Windows.

Pascal Bourguignon

unread,
Sep 7, 2004, 10:18:46 PM9/7/04
to
Fred Gilham <gil...@snapdragon.csl.sri.com> writes:
> (defun divisible-by-10-p (number)
> (zerop (mod number 11)))
> (defun list-multiples-of-10 ()
> (do ((number 0 (1+ number)))
> ((> number 100000))
> (when (divisible-by-10-p number)
> (format t "~A is divisible by 10.~%" number)
> (force-output)
> (sleep 1))))
> 99 is divisible by 10.
> (defun divisible-by-10-p (number)
> (zerop (mod number 10)))
> ; Converted DIVISIBLE-BY-10-P.
> ; Compiling DEFUN DIVISIBLE-BY-10-P:
> 100 is divisible by 10.
>
>
> This all happened without entering the debugger at all --- at least
> visibly.

It did not enter the debugger at all. It only works because of threads.

If you had:


(defun list-multiples-of-10 ()
(do ((number 0 (1+ number)))
((> number 100000))

(when (zerop (mod number 11))


(format t "~A is divisible by 10.~%" number)
(force-output)
(sleep 1))))

you could have changed list-multiples-of-10, but in the running thread
it would still be the older.

In any case, it shows the coolness of threads in lisp.

Joel Ray Holveck

unread,
Sep 7, 2004, 10:55:59 PM9/7/04
to
> So let me be sure I understand this: when my code stops in the
> debugger due to an error condition, I should be able to redefine the
> function in which the error occurred, and restart execution at the
> point of the failed call? Without having to restart my entire
> program? That sounds really useful, but it's not obvious exactly how
> to accomplish it at the debug prompt (and I assume this is also
> implementation-specific, but it's not obvious in any of the CLs
> I've tried so far).

As you surmise, yes, it is implementation-specific. Most things in
the debugger are, since what's possible varies wildly among
implementations. Here, I use two Lisp implementations: CMUCL and
LispWorks. LispWorks supports restarting function calls, and
returning from function calls, but CMUCL doesn't (AFAIK). From
looking at SLIME, I'd say that it supports returning, but not
restarting.

Kind of.

There's a mechanism that lets you get around this limitation, called
the conditions system. This is a part of ANSI Common Lisp (it wasn't
a part of first-edition CL, but everybody's gone ANSI nowdays). It
lets you do all sorts of cool things with errors. I'll discuss that
later, but first I'll tell you a little more about debugging.

> I am, BTW, using SLIME, but I'd also like to understand how to do
> these magic debug things at the (various) REPL(s).

I'm going to focus on SLIME here, but most debuggers will give you the
following options, at a bare minimum:
* Backtrace
* Move up a frame
* Move down a frame
* Examine frame's locals
* Help

Usually, help is on something like ? or :? or h or :h, and most
debuggers will tell you which it is before they give you the first
prompt. If you're confronted with a debugger you've never seen
before, hit its help key and look for those four functions (not
including help) first. Then decide what else you want to do. Most
debuggers have some advanced options; it's okay if you don't
understand them at first, since they may deal with the internals of
the system. But I would encourage you to experiment. There's a
simple program you can use to experiment with debuggers below, or
write your own.

As far as SLIME goes, that can restart frames on ABCL, Allegro, CLISP,
LispWorks, and OpenMCL (which is for the Mac). Note that the free
editions of the commercial Lisps might not have easy-to-access TTY
interfaces that you can use with Swank, but I could be wrong.

Anyway, I'll assume that you have a Lisp you can restart frames with.
I'm using LispWorks here, so that's what I'll discuss in this example.

Take a look over the following code. It should be pretty
straightforward. There's a lot of function call nesting on purpose,
since I want to get some frames that you can play with in the
debugger.

;;; This implements a basic trip to Jupiter.
;;;
;;; It's designed to put a few stack frames in before issuing an
;;; error, so that we can play with the debugger.
;;;
;;; Yes, it's contrived, and doesn't follow the plot of the movie.
;;; Sue me.

;; These are the global variables that determine what the ship should
;; do at any time. Remember that if you reload this file, they won't
;; get reset, although using SLIME's slime-eval-defun function
;; (usually on C-c C-c, I think) they will. You may prefer to
;; setq these at the top of MAIN-LOOP or use DEFPARAMETER forms.
(defvar *distance-from-jupiter* 5
"Distance from the ship to Jupiter, in jigibloits.")
(defvar *failed-components* '()
"List of ship components that have failed and must be repaired.")
(defvar *aggressors* '()
"List of entities that are attempting to stop us from reaching Jupiter.")

(defun main-loop ()
"Go to Jupiter, and handle any problems along the way."
(loop
while (> *distance-from-jupiter* 0)
do (format t "~&Distance from Jupiter: ~A" *distance-from-jupiter*)
if *failed-components*
do (repair (first *failed-components*))
else
do (fly-to-jupiter))
(format t "~&We got to Jupiter."))

(defun fly-to-jupiter ()
"Fly one jigibloit closer to Jupiter."
(format t "~&We're going to Jupiter!")
(force-output)
(sleep 1)
(decf *distance-from-jupiter*)
;; Super secret thing: When we're almost at Jupiter, freak out and
;; pretend that there's a malfunction.
(when (= *distance-from-jupiter* 1)
(push 'ae35 *failed-components*)))

(defun repair (item)
"Dispatch a crewman to repair or replace ITEM.
Also, if there's anything attacking us that might have caused the damage,
deal with that too."
(send-out-repairman item)
(check-for-aggressors)
(setq *failed-components* (remove item *failed-components*)))

(defun send-out-repairman (item)
(format t "~&There seems to be a fault in the ~A unit. Go fix it." item)
;; Super secret thing: Dave thinks the computer is malfunctioning,
;; so he starts attacking the computer after we send out a crewman.
(push 'dave *aggressors*))

(defun check-for-aggressors ()
"Deal with any current aggressors."
(format t "~&Checking for aggressors.")
(loop
for aggressor in *aggressors*
do (handle-aggressor aggressor))
(setq *aggressors* '()))

(defun handle-aggressor (aggressor)
"Confront AGGRESSOR."
;; Note that confronting Dave is a fatal error. After all, if Dave
;; could have just hit the "Continue" button, things would have been
;; peachy, right?
(error "What are you doing, ~A?" aggressor))

This code was painstakingly designed to be the optimal testing
scenario... okay, it was thrown together in a couple of minutes while
I had 2001 on my mind. But it does put down some stack frames and
then issue an error, and that's the important thing.

Look over the code and get a feel for how it works. Basically, things
go without incident until we're 1 jigabloit from Jupiter, at which
point we pretend there's a failure in the AE-35 radio. We send
somebody out to fix it, Dave gets suspicious and starts attacking, and
we issue a fatal error.

Now, using SLIME, start up a Lisp that supports frame restarting, such
as CLISP. Load up the file, and then run (main-loop) from the REPL.
You should see something like this:
We're going to Jupiter!
We're going to Jupiter!
We're going to Jupiter!
We're going to Jupiter!

When suddenly:
There seems to be a fault in the AE35 unit. Go fix it.
Checking for aggressors.

At this point, our program has discovered that Dave is trying to shut
us down and issues an error. SLIME shows us the error in a new window:

What are you doing, DAVE?
[Condition of type SIMPLE-ERROR]

Restarts:
0: [ABORT] Abort handling SLIME request.
1: [ABORT] Quit process.

Backtrace:
0: CONDITIONS::CONDITIONS-ERROR (:INVISIBLEP T "What are you do...
1: HANDLE-AGGRESSOR (DAVE)
2: CHECK-FOR-AGGRESSORS NIL
3: REPAIR (AE35)
4: MAIN-LOOP NIL
5: SPECIAL::%EVAL-NOHOOK ((MAIN-LOOP))
6: IV:PROCESS-TOP-LEVEL (DSPEC:*CURRENT-FORM* #S(LEXICAL::ENVIR...
-- more --

So, let's look at what we have. First, there's the error message,
followed by the type of error. A SIMPLE-ERROR is what you get if you
use (error "Message"). I'll discuss other types of errors later.

Next, you have the restarts. Those are the "what can I do now"
messages.

Finally, there's the backtrace. The precise format of the backtrace
may vary depending on your Lisp implementation. Also, the top few
frames may be part of the debugger or conditions system, and the
bottom few may be part of your Lisp or SLIME. By and large, sldb (the
SLIME debugger) prunes out most of the irrelevant frames, though.

So, what can you do from this? Well, obviously, you can use one of
the two ABORT restarts to either stop (main-loop) or Lisp. But then
we'll be stranded 4/5 of the way to Jupiter, so let's look at
alternatives.

We can move down to the backtrace. You can use your arrow keys or
C-n / C-p to move around the buffer like you usually do. But you can
also use "n" and "p" to move around the backtrace. If you want to see
the details (specifically, the local variables and the source), you
can use M-n and M-p.

You can also use "e" to evaluate something. It gets evaluated in the
frame you're sitting on, so you have its lexical variables (and other
lexically scoped entities) at your disposal (unless they've been
optimized away).

Now, move to the CHECK-FOR-AGGRESSORS line. If they're not showing,
press "t" to show its lexical variables. For me, on LispWorks, it
lists AGGRESSOR = DAVE, while on CMUCL it lists
AGGRESSOR = :<NOT-AVAILABLE> instead. No big deal.

We want to tell the computer that there are no aggressors. So press
"e". Then, at the prompt, type (setq *aggressors* nil) and press RET.
If you want to verify that it took, you can press "e" to evaluate
*aggressors* now.

Finally, we need to restart the aggressors check. If you're not still
there, arrow to the CHECK-FOR-AGGRESSORS line, and then press "r".
The error vanishes, and your REPL now continues:
Checking for aggressors.
We're going to Jupiter!
We got to Jupiter.

This time, the CHECK-FOR-AGGRESSORS test didn't see any aggressors, so
it let us proceed with our Jupiter mission.

I'd encourage you to spend some time playing with the SLIME debugger,
and your Lisp's built-in debugger. In the SLIME debugger, you can use
a capital "B" to switch to your Lisp's debugger. You can also use
"C-h m" to tell what commands are available in the SLIME debugger,
although remember that not all commands are available on all Lisps.

Digest all that a little while, and then I'll tell you about how a
program can determine its own error-handling destiny: the Common Lisp
Conditions System.

;;;

See, most languages have a very simplistic exception-handling system.
In increasing order of sophistication, C's setjmp and longjmp are just
primitives, and the language doesn't define anything on top of that.
Perl has eval{die}, C++ and Java have a try...catch...finally. Common
Lisp has the Conditions System.

The first thing you should know is this. You don't need to understand
the conditions system to write Lisp code, so don't feel discouraged if
you don't get it at first. It takes most programmers a while to get
the idea of conditions. Second, you don't need to understand the
entire conditions system to use parts of it.

Now, that said, the conditions system is a way to have your program
decide should be done after an error. It can do this automatically,
with user input, or with a combination of the two.

For example, wouldn't it be great if Dave could have just told the
computer to ignore the error and go on about its business? Well, you
can: this is what we refer to as a "continuable error". In the
comments to HANDLE-AGGRESSOR, I hinted at a "Continue" button, and
that's exactly what CERROR gives you. It's the same as ERROR, but it
lets the user continue as if the error didn't happen. Change the
definition of HANDLE-AGGRESSOR to the following:

(defun handle-aggressor (aggressor)
"Confront AGGRESSOR."
(cerror "What are you doing, ~A?" aggressor))

Now reset your global variables and try running MAIN-LOOP again. This
time, you should have a new option in the debugger that looks
something like this:

0: [CONTINUE] Ignore the aggressor

If you select this option (by using "c" which select the topmost
[CONTINUE] option, by pressing its number, by moving to it and
pressing RET, or by middle-clicking on it), then the program will
continue from the point of the error. CHECK-FOR-AGGRESSORS will clear
the *AGGRESSORS* list, and we'll get to Jupiter just fine.

This new option is what's called a "restart". I have three restarts
for this error, although your system may vary:
0: [CONTINUE] Ignore the aggressor
1: [ABORT] Abort handling SLIME request.
2: [ABORT] Quit process.

Restarts don't necessarily restart a computation, although they can.
In our case, though, none of the restarts do. A restart is simply a
path of code that resumes computation after an error.

The Lisp system typically defines some restarts, and SLIME defines its
own restart every time you enter a request (such as at a SLIME REPL).
You can also write your own restart. In the next bit of code, we
write a restart that will ignore all the aggressors.

(defun main-loop ()
"Go to Jupiter, and handle any problems along the way."
(loop
while (> *distance-from-jupiter* 0)
do (format t "~&Distance from Jupiter: ~A" *distance-from-jupiter*)
do (check-for-aggressors)
if *failed-components*
do (repair (first *failed-components*))
else
do (fly-to-jupiter))
(format t "~&We got to Jupiter."))

(defun check-for-aggressors ()
"Deal with any current aggressors."
(restart-case
(progn
(format t "~&Checking for aggressors.")
(loop
for aggressor in *aggressors*
do (handle-aggressor aggressor))
(setq *aggressors* '()))
(nil ()
:report "Forget about all the aggressors."
(setq *aggressors* '()))
(nil ()
:report "Don't handle any aggressors, but don't forget them."
nil)))

Here, we've made two changes. The first change is that we added a
CHECK-FOR-AGGRESSORS test into the main loop, so that the effects of
the two different restarts are apparent.

The second, substantial change is that we added restarts around
CHECK-FOR-AGGRESSORS. RESTART-CASE lets us give a form to be
evaluated, in this case, the PROGN form. Then, we give a list of
restarts. Each restart has an optional name (we used NIL here), a
list of arguments (none of ours take any), and how the restart should
be shown in the restart list. Then, you have code to execute when the
restart is invoked: in one case we clear the aggressor list, and in
the other we don't have anything to do, so we just use NIL for our
code. (That could be omitted, actually.)

If any errors crop up in the PROGN form, the user will have the option
to select one of our two new restarts, as well as the old ones
(continue and two aborts). The restarts are only in place within the
PROGN; once it exits, the restarts aren't in place anymore.

Now, perhaps we want to make sure that the crew is suitably warned if
we exit CHECK-FOR-AGGRESSORS while there are still aggressors on the
list. At first thought, you might just write the code in the body of
the first restart, but there's a problem. If you use an ABORT
restart, that causes CHECK-FOR-AGGRESSORS to exit without going
through that code. The program may have other restarts that are
established higher up the call chain that would also have this
effect.

To deal with this-- where you have code that must be executed no
matter how you exit-- you can use UNWIND-PROTECT (the name comes from
the use of tapes to store the call stack). Here's our
CHECK-FOR-AGGRESSORS, this time with an UNWIND-PROTECT around it.

(defun check-for-aggressors ()
"Deal with any current aggressors."
(unwind-protect
(restart-case
(progn
(format t "~&Checking for aggressors.")
(loop
for aggressor in *aggressors*
do (handle-aggressor aggressor))
(setq *aggressors* '()))
(nil ()
:report "Forget about all the aggressors."
(setq *aggressors* '()))
(nil ()
:report "Don't handle any aggressors, but don't forget them."
nil))
(when *aggressors*
(warn "Still have aggressors ~A" *aggressors*))))

The UNWIND-PROTECT evaluates its first form (the RESTART-CASE), and
makes sure that the remaining "cleanup" forms (the WHEN bit) get
evaluated, even if there's an abnormal termination. Run this and see
what happens when you use an abort restart instead.

Well, this is all well and good, but your question was about
restarting your function calls in a Lisp that doesn't support that.
Since restarts can be arbitrary code, you can create a restart that
calls your function again (presumably after you've done some stuff in
the debugger first). Here's an example of doing that:

(defun check-for-aggressors ()
"Deal with any current aggressors."
(unwind-protect
(restart-case
(progn
(format t "~&Checking for aggressors.")
(loop
for aggressor in *aggressors*
do (handle-aggressor aggressor))
(setq *aggressors* '()))
(nil ()
:report "Check for aggressors again."
(check-for-aggressors))
(nil ()
:report "Forget about all the aggressors."
(setq *aggressors* '()))
(nil ()
:report "Don't handle any aggressors, but don't forget them."
nil))
(when *aggressors*
(warn "Still have aggressors ~A" *aggressors*))))

In the above implementation, we created a restart that simply calls
check-for-aggressors again.

You can imagine applying this to a large test harness: with a few
judicious restarts, you can fix and retest your system while you're
still in the regression run.

Putting restarts into the regression runner may be easy, but you might
on occasion need to put similar restarts around a lot of functions for
a bit of debugging. So to make that easier (for you and me), I've
written a little function that wraps a such a restart around an
existing function. It's a little bit more advanced than what we've
been talking about, but you can use it for now and study it later if
you want to.

(defun make-retryable (fn-symbol)
(check-type fn-symbol symbol)
(let ((original-fn (symbol-function fn-symbol)))
(labels
((new-function (&rest args)
(restart-case
(apply original-fn args)
(retry ()
:report (lambda (stream)
(format stream "Retry call to ~A" fn-symbol))
(apply #'new-function args)))))
(setf (symbol-function fn-symbol) #'new-function))))

You use it after you've defun'd the function, like so:

(defun handle-aggressor (aggressor)
"Confront AGGRESSOR."
(cerror "Ignore the aggressor" "What are you doing, ~A?" aggressor))
(make-retryable 'handle-aggressor)

There are a couple of disadvantages to this, though. First, you have
to execute the MAKE-RETRYABLE call any time you re-execute the DEFUN,
or the restart won't be there. Second, this gets rid of any function
documentation or argument list information stored with the function.
So you know how you can ask SLIME to give you the arguments to a
function, or its documentation? That won't work on a function that
MAKE-RETRYABLE has processed. These problems are fixable, such as
with a macro around DEFUN, but I'll leave that up to your imagination.

I've talked about restarts, since they're the focus of what you
asked. But the condition system provides much more. Using it, you
can:

* Create new types of errors, with their own associated information.
This is built on CLOS, the OOP system in CL. For example, a web
browser might define a URL-UNREADABLE error with URL and HTTP-CODE
slots. It might also define subclasses, such as URL-NOT-FOUND and
URL-FORBIDDEN.
* Define restarts that are only applicable in particular situations,
or with particular types of errors. For example, a restart may
only be applicable to URL-FORBIDDEN errors.
* Define restarts that take arguments from the user when invoked,
using whatever interface the debugger might provide, or an interface
provided by the restart. For example, a restart that is applicable
to URL-FORBIDDEN errors might take authentication information as
arguments.
* Install handlers that decide what action to take (such as calling a
restart, or performing arbitrary calculations) instead of asking the
user to do so. For example, a handler for URL-FORBIDDEN might
automatically invoke the restart we've discussed in an interactive
session, but decline to handle it in a non-interactive session (at
which point a higher-level handler or debugger can handle it).
That's why restarts can be given names: so that handlers can invoke
them automatically by name.
* Replace the system debugger with one of your own design. This is
how SLIME's sldb works.

A common use of the condition system is to handle certain errors a
certain way, so that a "file not found" error opens a blank document
while other errors (such as arithmetic errors) get passed to the
debugger.

In my case, I write application servers in Lisp. When my code gets an
error while processing a request, I don't want the server console to
pop into the debugger, but I still want the user to know that an error
occurred, and what it was. So I have an error handler near the top of
my client handler that sends the error message to the user and then
invokes the ABORT restart. I also have an ABORT restart a little bit
above that which terminates the current request (instead of
terminating the entire system or something). If I'm debugging and
want to use the Lisp debugger, I can turn off the handler but still
have the ABORT restart.

Well, I think I've given you enough to think about on this topic for a
while. Even though most of this document discussed the condition
system, it's not something you have to think about often. Most Lisp
programs only define some error types, and the more sophisticated ones
may have some restarts and/or handlers defined around the event loop.
But when you need it-- such as when you're writing a regression test
harness-- it's great to have.

Cheers,
joelh

PS: In case it wasn't clear-- and I couldn't tell from your question
if this was clear or not-- you can enter a new function definition at
the debug prompt by just typing in your defun form:

0] <-- debugger prompt
(defun foo () (+ foo 1))

You can also execute whatever Lisp code you want:

0] (+ 1 1)
2

You're not just limited to the debugger commands.

Of course, to load in a new definition, you can use SLIME's commands
too and not worry about the debugger prompt.

Rober...@yahoogroups.com

unread,
Sep 7, 2004, 11:46:57 PM9/7/04
to
> From: Joe Knapka <jkn...@kneuro.net>

> So let me be sure I understand this: when my code stops in the
> debugger due to an error condition, I should be able to redefine the
> function in which the error occurred, and restart execution at the
> point of the failed call? Without having to restart my entire program?

It really depends on whether you've written that function (and the
innerds of it) in a "functional programming" style, that is no
side-effects, it gets a starting state via the parameters and returns
an ending state via return values, or whether you've written that
function in a grossly non-functional style whereby it immediately
starts causing side-effects on global variables or destructively
modifying the parameters that were passed to it. In the former case,
yes, you can easily give the new re-defined version of that same
function the same original parameters it had been given before, and
watch what it does. In the latter case, no you need to re-create the
stuff your function has already changed before you can get back to the
original state to call your function again, which may require
re-executing some higher-up function, or re-starting your entire D/P
process, or if you've already destructively modified disk files then
you may have to roll back those disk files to backups before you can
recover the original state to re-try the edited function in the
re-started D/P process.

This difference is one big reason why mostly-functional programming is
a good idea compared to mostly-side-effect programming.

Joe Knapka

unread,
Sep 8, 2004, 1:22:06 AM9/8/04
to

Joel, a giant economy-size box of "Thanks" is on its way to you via parcel
post. Unfortunately, SLIME+CLISP does not behave as expected for me :-(((
Hopefully someone can tell me why; see below for gory details.

Joel Ray Holveck <jo...@juniper.net> writes:

[snip]

I'm with you until this point. The backtrace I get from CLISP looks
totally different; the one you quote above makes sense to me, but the
one I see in SLIME doesn't. In fact I see only one frame clearly
related to the call stack of the "Jupiter" code. Here's my SLIME error
buffer :

What are you doing, DAVE?
[Condition of type SIMPLE-ERROR]

Restarts:
0: [ABORT] Abort handling SLIME request.

Backtrace:
0: frame binding variables (~ = dynamically):
| ~ SYSTEM::*FASOUTPUT-STREAM* <--> NIL
1: EVAL frame for form (MAIN-LOOP)
2: EVAL frame for form (SWANK:LISTENER-EVAL "(main-loop)
")
3: EVAL frame for form (SWANK:START-SERVER "/tmp/slime.5886")
4: EVAL frame for form (SWANK:START-SERVER "/tmp/slime.5886")

Nearly all of this looks like debugger cruft. And SLIME's "view"
operation claims it can't find the source for any frame, not even
(MAIN-LOOP). (Incidentally, I //did// save the "Jupiter" code to a
file and ^C^K it; I didn't just paste it into the listener.)

Any idea what might be up with this? Presumably I've got something
mis-configured, but I've no idea what. FYI, platform is Red Hat 8,
CLISP is v.2.31, Emacs is Gnu v.21.2.1 (I have the latest Xemacs
waiting in the wings to be built and installed, tho). SLIME is
yesterday's CVS.

I'm going to try with CMUCL and see if things go any better.

Christophe Rhodes

unread,
Sep 8, 2004, 2:49:49 AM9/8/04
to
Joe Knapka <jkn...@kneuro.net> writes:

> Joel, a giant economy-size box of "Thanks" is on its way to you via parcel
> post. Unfortunately, SLIME+CLISP does not behave as expected for me :-(((
> Hopefully someone can tell me why; see below for gory details.

CLISP has a notoriously debug-unfriendly compiler. Trying
more-or-less any other implementation is a good bet.

Christophe

Thomas Schilling

unread,
Sep 8, 2004, 5:41:54 AM9/8/04
to
Pascal Bourguignon wrote:

> you could have changed list-multiples-of-10, but in the running thread
> it would still be the older.

I recently noticed that. Is there a way to modify a specific thread?

More exactly: I started some code (in allegro CL) with
(mp:process-run-function ...) but couldn't change it's behaviour by
changing function defs and C-c C-c'ing them (until exiting and restarting
the code).

Antonio Menezes Leitao

unread,
Sep 8, 2004, 7:42:31 AM9/8/04
to
Hi,

A few years ago I wrote a debugging example so that my students could
have some sort of guide for their debugging sessions while using
Allegro CL and its great Emacs interface.

Unfortunately, it was written in Portuguese which I'm afraid very few
people in this newsgroup understand. Today, I translated it (almost
literally) to English.

You can take a look at:

http://www.gia.ist.utl.pt:~aml/debugging-allegro.ps

I didn't have the time to read it carefully so you might find
unintended errors (in the middle of lots if intended errors).

Obviously, comments are welcome.

If I have the time, I might try to adapt it to SLIME but, currently,
not all Common Lisps support the debugging operations that I use in
the example (and that I really miss).

Best regards,

António Leitão.

Duane Rettig

unread,
Sep 8, 2004, 11:02:03 AM9/8/04
to
Antonio Menezes Leitao <Antonio...@evaluator.pt> writes:

> Hi,
>
> A few years ago I wrote a debugging example so that my students could
> have some sort of guide for their debugging sessions while using
> Allegro CL and its great Emacs interface.
>
> Unfortunately, it was written in Portuguese which I'm afraid very few
> people in this newsgroup understand. Today, I translated it (almost
> literally) to English.
>
> You can take a look at:
>
> http://www.gia.ist.utl.pt:~aml/debugging-allegro.ps

Is this URL correct? Or perhaps there is a permissions
problem somewhere? I had trouble accessing it with my
browser (mozilla) got a server error saying that the
debugging-allegro.ps was not on the www.gia.ist.utl.pt
server.

> I didn't have the time to read it carefully so you might find
> unintended errors (in the middle of lots if intended errors).
>
> Obviously, comments are welcome.
>
> If I have the time, I might try to adapt it to SLIME but, currently,
> not all Common Lisps support the debugging operations that I use in
> the example (and that I really miss).

Perhaps a debugger api would be a good candidate for de facto
standardization for such an effort. Our debugger-api (found at
http://www.franz.com/support/documentation/6.2/doc/debugger-api.htm)
is not really Franz' invention - attributions are given at the
beginning of the document - it was specified by Sun Mirosystems in
the late '80s and implemented by at least Franz and Lucid in a project
where we were vying to become the next Sun Common Lisp. Sun kept
Lucid, but we both kept the debugger-api, for the most part, with
some refining through the years (some parts were useless to us and
we left them out or reduced their functionality, and there was some
missing functionality as well that we added, but the parts we left
out have always been reinstatable by requiring :sundebug). Perhaps
this document would be a good starting point for a common debugger
api, in order to get common tools to work universally...

--
Duane Rettig du...@franz.com Franz Inc. http://www.franz.com/
555 12th St., Suite 1450 http://www.555citycenter.com/
Oakland, Ca. 94607 Phone: (510) 452-2000; Fax: (510) 452-0182

Raistlin Magere

unread,
Sep 8, 2004, 11:05:51 AM9/8/04
to

"Duane Rettig" <du...@franz.com> wrote in message
news:4oekgb...@franz.com...

> Antonio Menezes Leitao <Antonio...@evaluator.pt> writes:
>
> > Today, I translated it (almost literally) to English.
> >
> > You can take a look at:
> >
> > http://www.gia.ist.utl.pt:~aml/debugging-allegro.ps
>
> Is this URL correct? Or perhaps there is a permissions
> problem somewhere? I had trouble accessing it with my
> browser (mozilla) got a server error saying that the
> debugging-allegro.ps was not on the www.gia.ist.utl.pt
> server.
>

The URL is wrong instead of the : after pt there should be a /
i.e. http://www.gia.ist.utl.pt/~aml/debugging-allegro.ps


Paolo Amoroso

unread,
Sep 8, 2004, 9:49:27 AM9/8/04
to
Joel Ray Holveck <jo...@juniper.net> writes:

> implementations. Here, I use two Lisp implementations: CMUCL and
> LispWorks. LispWorks supports restarting function calls, and
> returning from function calls, but CMUCL doesn't (AFAIK). From

The current CMUCL CVS sources support returning from function calls.


Paolo
--
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Recommended Common Lisp libraries/tools (Google for info on each):
- ASDF/ASDF-INSTALL: system building/installation
- CL-PPCRE: regular expressions
- UFFI: Foreign Function Interface

Fred Gilham

unread,
Sep 8, 2004, 11:33:31 AM9/8/04
to

> > This all happened without entering the debugger at all --- at
> > least visibly.
>
> It did not enter the debugger at all. It only works because of
> threads.

SLIME may in fact work like that --- though I believe it can be
configured to work without using threads.

But I first learned about this redefinition-while-running trick using
ILISP, which certainly doesn't use threads. I believe ILISP does
actually force a break on Lisp and enter the redefinition "behind the
scenes."

It still seems like still magic no matter how it's done. :-)

--
Fred Gilham gil...@csl.sri.com
The TMI accident was unique: it was the only multi-billion dollar
accident in history in which nobody was harmed.
-Howard C. Hayden, Professor Emeritus, U of Conn.

Antonio Menezes Leitao

unread,
Sep 8, 2004, 8:22:42 AM9/8/04
to
Antonio Menezes Leitao <Antonio...@evaluator.pt> writes:

> You can take a look at:
>
> http://www.gia.ist.utl.pt:~aml/debugging-allegro.ps
>

Let me debug this :-)

The correct URL is

http://www.gia.ist.utl.pt/~aml/debugging-allegro.ps

Best regards,

António Leitão.

Camm Maguire

unread,
Sep 8, 2004, 1:06:30 PM9/8/04
to
Greetings!

How do you do file i/o, or any function which operates on a stream,
functionally?

Take care,

Rober...@YahooGroups.Com (rem...@Yahoo.Com) writes:

--
Camm Maguire ca...@enhanced.com
==========================================================================
"The earth is but one country, and mankind its citizens." -- Baha'u'llah

mikel

unread,
Sep 8, 2004, 1:48:57 PM9/8/04
to
Camm Maguire wrote:
> Greetings!
>
> How do you do file i/o, or any function which operates on a stream,
> functionally?

With monads or uniqueness types. Googling for either one will turn up
many references.

Vassil Nikolov

unread,
Sep 8, 2004, 9:21:17 PM9/8/04
to
Pascal Bourguignon <sp...@mouse-potato.com> writes:


Or with the original code, but DIVISIBLE-BY-10-P inlined.


---Vassil.


--
Vassil Nikolov <vnik...@poboxes.com>

Hollerith's Law of Docstrings: Everything can be summarized in 72 bytes.

Vassil Nikolov

unread,
Sep 8, 2004, 9:35:46 PM9/8/04
to
Joel Ray Holveck <jo...@juniper.net> writes:
> [...]

> See, most languages have a very simplistic exception-handling system.
> In increasing order of sophistication, C's setjmp and longjmp are just
> primitives, and the language doesn't define anything on top of that.
> Perl has eval{die}, C++ and Java have a try...catch...finally. Common
> Lisp has the Conditions System.


Just a footnote for completeness: Common Lisp also has CATCH/THROW,
which are more primitive than, and predate the Condition System.
The analogy isn't exact, of course, but C++/Java's
try/catch/throw/finally are _much_ more like Common Lisp's
CATCH/THROW/UNWIND-PROTECT than the (way more sophisticated)
Condition System.

Alain Picard

unread,
Sep 9, 2004, 6:11:36 AM9/9/04
to
Thomas Schilling <tjs...@yahoo.de> writes:

It makes a big difference if the code in the other
thread is doing something like

(loop
....
(funcall #'foo) ;; It has an anonymous compiled function object, we're stuffed
...)

vs

(loop
....
(funcall 'foo) ;; It indirects on symbol-function of FOO; we can redefine that!
...)

Also, you cant affect the code INSIDE of function FOO while FOO
is running, of course.

Alexey Dejneka

unread,
Sep 9, 2004, 1:31:20 PM9/9/04
to
Alain Picard <Alain....@memetrics.com> writes:

> (loop
> ....
> (funcall #'foo) ;; It has an anonymous compiled function object, we're stuffed
> ...)
>
> vs
>
> (loop
> ....
> (funcall 'foo) ;; It indirects on symbol-function of FOO; we can redefine that!
> ...)

Where does CLHS say that FUNCTION is constant-foldable?

--
Regards,
Alexey Dejneka

"Alas, the spheres of truth are less transparent than those of
illusion." -- L.E.J. Brouwer

Matthew Danish

unread,
Sep 9, 2004, 1:55:10 PM9/9/04
to
On Wed, Sep 08, 2004 at 01:06:30PM -0400, Camm Maguire wrote:
> How do you do file i/o, or any function which operates on a stream,
> functionally?

Search message ID 2004090200...@mapcar.org for an example done
in Common Lisp.

--
;;;; Matthew Danish -- user: mrd domain: cmu.edu
;;;; OpenPGP public key: C24B6010 on keyring.debian.org

Vassil Nikolov

unread,
Sep 9, 2004, 9:09:13 PM9/9/04
to
Alexey Dejneka <adej...@comail.ru> writes:

> Alain Picard <Alain....@memetrics.com> writes:
>
>> (loop
>> ....
>> (funcall #'foo) ;; It has an anonymous compiled function object, we're stuffed
>> ...)
>>
>> vs
>>
>> (loop
>> ....
>> (funcall 'foo) ;; It indirects on symbol-function of FOO; we can redefine that!
>> ...)
>
> Where does CLHS say that FUNCTION is constant-foldable?


Well, it says that the value is "the functional value of" the
argument, and then "functional value" is "the contents of the
function cell" involved (extracting pieces from the definition of
the FUNCTION special operator and from the Glossary). So if FV is
bound to the value of (FUNCTION F), and (DEFUN F ...) then redefines
F, (FUNCALL FV) would still invoke the old definition, because it
"picked up" the old contents of F's function cell.

(I don't think that has anything to do with constant folding,
though.)

Alexey Dejneka

unread,
Sep 9, 2004, 11:39:17 PM9/9/04
to
Vassil Nikolov <vnik...@poboxes.com> writes:

> Alexey Dejneka <adej...@comail.ru> writes:
>
> > Alain Picard <Alain....@memetrics.com> writes:
> >
> >> (loop
> >> ....
> >> (funcall #'foo) ;; It has an anonymous compiled function object, we're stuffed
> >> ...)
> >>
> >> vs
> >>
> >> (loop
> >> ....
> >> (funcall 'foo) ;; It indirects on symbol-function of FOO; we can redefine that!
> >> ...)
> >
> > Where does CLHS say that FUNCTION is constant-foldable?
>
>
> Well, it says that the value is "the functional value of" the
> argument, and then "functional value" is "the contents of the
> function cell" involved (extracting pieces from the definition of
> the FUNCTION special operator and from the Glossary). So if FV is
> bound to the value of (FUNCTION F), and (DEFUN F ...) then redefines
> F, (FUNCALL FV) would still invoke the old definition, because it
> "picked up" the old contents of F's function cell.
>
> (I don't think that has anything to do with constant folding,
> though.)

Well, if the examples above were written as

(let ((f #'foo)) ; vs 'foo
(loop
...
(funcall f)
...))

I would agree with you. But Alain's code "calls" FUNCTION on every
iteration, and so, IMHO, retakes functional value of FOO on every
iteration too.

Vassil Nikolov

unread,
Sep 10, 2004, 12:17:10 AM9/10/04
to
Alexey Dejneka <adej...@comail.ru> writes:


Sorry I misunderstood you. Yes, you are right. I must have
subconsciously perceived the former as if it was written in the
latter way (which must be the idea behind the example, I think).

Rober...@yahoogroups.com

unread,
Sep 10, 2004, 1:16:14 PM9/10/04
to
> From: Camm Maguire <ca...@enhanced.com>

> How do you do file i/o, or any function which operates on a stream,
> functionally?

I presume you mean "mostly functionally", per what I said earlier.

Generally it's a bad idea to make lots of destructive changes to an
existing file. If possible you shouldn't modify an existing file at
all, but rather should make a new version of the file and later rename
the files so the previous working version becomes a backup and the new
version becomes the working version. But if you really must update a
single file in-place, you do "transaction processing" whereby you set
up a whole transaction as a unit without changing the file at all, then
in a single "atomic" process you perform the transaction upon the file.

Either way, most of the processing can be functional before you start
to create the new version of the file or perform the atomic in-place
update. If something goes wrong you re-do whatever calculations you
were trying until you get them right. Finally you have the data for a
new version ready to write out, or the data for a transaction ready to
perform. Using file versions, if you screw up while writing the new
version, and you need to re-do that, just delete the file and start
over. The only place where a crash can leave you with no way to restore
the original state of the file(s) is if there's a power failure during
the moment of renaming the file versions causing the file system to
become corrupt, or if something goes wrong in the middle of an "atomic"
transaction being applied to the file.

If you're communicating with another process via a stream, using
"atomic" transactions may be best. Of course you want to use a
communications protocol which can tolerate occasional disruptions such
as a lost or duplicated packet.

Summary: Of course you can't make the process totally functional, but
you can isolate the non-functional operations to specific update times
and have most of the rest functional, so that most bugs occur during
the functional majority which can be easily re-tried as needed.

Alain Picard

unread,
Sep 11, 2004, 7:43:08 AM9/11/04
to
Alexey Dejneka <adej...@comail.ru> writes:

>
> Well, if the examples above were written as
>
> (let ((f #'foo)) ; vs 'foo
> (loop
> ...
> (funcall f)
> ...))
>
> I would agree with you. But Alain's code "calls" FUNCTION on every
> iteration, and so, IMHO, retakes functional value of FOO on every
> iteration too.

You are, of course, completely correct. Sorry about
the confusing and mistaken example.

Rahul Jain

unread,
Sep 17, 2004, 12:34:39 AM9/17/04
to
Greg Menke <gregm...@toadmail.com> writes:

> That said, sometimes I also miss a nice step-into, step-over interface
> when debugging Lisp code.

Why is that? I have one and use it occasionally. (in CMUCL)

--
Rahul Jain
rj...@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist

Marek Stefan

unread,
Sep 17, 2004, 11:18:02 AM9/17/04
to
> Hi everyone,
>
> I am getting into Lisp again after a brief exposure in college,
> followed by a lengthy absence (> 10 years). I seem to be missing the
> blindingly obvious. I've now got a few CLs installed on my Linux box
> (gcl, clisp, sbcl), and after poking around for hours in the man
> pages, this group's archive and FAQ, Google, and Paul Graham's "ANSI
> Common Lisp", I haven't been able to find any way to set a breakpoint,
> in any of the CLs I've installed. (trace) is nice, but I'd like a way
> to say, "please stop any time my-func is evaluated," and then end up
> at a prompt where I can see what's about to be evaluated, step over or
> into forms, inspect objects, and so forth. Does such a thing exist?
>
> Please forgive me if this is a FAQ. A pointer to relevant online
> documentation would be more than sufficient.
>
> Thanks,

>
> -- Joe
>
> --
> "We sat and watched as this whole <-- (Died Pretty -- "Springenfall")
> blue sky turned to black..."
> ... Re-defeat Bush in '04.
> --
> pub 1024D/BA496D2B 2004-05-14 Joseph A Knapka
> Key fingerprint = 3BA2 FE72 3CBA D4C2 21E4 C9B4 3230 94D7 BA49
> 6D2B
> If you really want to get my attention, send mail to
> jknapka .at. kneuro .dot. net.


I have another question:
I also work with Matlab, which has quite nice feature (within the
integrated IDE) of graphical debugging (i.e., colourfully emphasizing
lines of the code being traced) of source files with step, step-into,
step-out, etc. I also use lisp (lispworks with emacs and ilisp) and
would appreciate something similar. I was wandering if there is any
package for emacs that would be able to do this. Does anybody know?

Thank's.

Marek

Greg Menke

unread,
Sep 17, 2004, 6:45:17 AM9/17/04
to

Rahul Jain <rj...@nyct.net> writes:
> Greg Menke <gregm...@toadmail.com> writes:
>
> > That said, sometimes I also miss a nice step-into, step-over interface
> > when debugging Lisp code.
>
> Why is that? I have one and use it occasionally. (in CMUCL)
>

Perhaps I have missed it in previous versions of Lispworks and not
realized its there. I've used the gui debugger in LW, but not for
tracing just to identifty the location of trouble.

I might well be the flea that learned to jump only as high as the lid
of the jar & didn't realize somebody took off the lid.

Gregm

niko...@random-state.net

unread,
Sep 17, 2004, 6:08:46 PM9/17/04
to
Greg Menke <gregm...@toadmail.com> wrote:

> Perhaps I have missed it in previous versions of Lispworks and not
> realized its there. I've used the gui debugger in LW, but not for
> tracing just to identifty the location of trouble.

FWIW, CVS SBCL has a new stepper that also supports step-into, etc, for
code compiled with high debug policy -- including display of intermediate
values. The tty stepper interface is "primitive" (aka barbaric, sucky, of
the deep end), but the hooks are all there for someone to write a pretty
Slime interface. (hint, nudge-nudge)

Cheers,

-- Nikodemus "Not as clumsy or random as a C++ or Java.
An elegant weapon for a more civilized time."

0 new messages