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

newbie: non local exits in CL

42 views
Skip to first unread message

Laurent Peron

unread,
Jul 8, 1997, 3:00:00 AM7/8/97
to

Hello,

I would need some help with CATCH / THROW / UNWIND-PROTECT forms in
Ansi Common Lisp.
My idea would be to simulate classical languages exception handling
mechanisms, ie execute some code because a given exception has been
raised. eg (informal, stupid and ugly syntax, yes I know) :

(try
<form to be evaluated>)
(catch 'error variable ;;; 'error is the tag
(progn <forms to execute when 'error has been caught
as the tag, and where `variable' is bound to
the value sent by THROW>)))

What I would like to have is to execute some code if THROW raised
an exception, and to write it in a clean and readable way.
It seems that currently I have to make THROW return a
special value, and test this value outside CATCH, ie

(if (eql <special-value> (catch 'error <form to try>))
(<form to execute because throw has sent the special value>))

where the THROW would be

(throw 'error <special-value>)

Furthermore, I need to be sure that the <form to try> will
never evaluate to my <special-value>.

I would appreciate advices about usage of THROW and CATCH.

I have started learning Common Lisp 3 weeks ago.

Thank you.

LP
--
My Amiga runs with AmigaOS + GNU, my PC with Linux + GNU (only, no Micro$oft)

Lauren...@emi.u-bordeaux.fr | Universite Bordeaux I. France
pe...@bordeaux.inra.fr | DESS Genie Logiciel
AmigaOS, Linux, pOS | Software Engineering DESS studies


Kent M Pitman

unread,
Jul 8, 1997, 3:00:00 AM7/8/97
to

In article <5ptmur$n...@news.u-bordeaux.fr> pe...@emi.u-bordeaux.fr
(Laurent Peron) writes:

I had difficulty understanding the question you're asking so am not
sure I can answer it directly. It seems to relate to a concern about
distinguishing whether the the exit from the CATCH is a normal exit
or the magic exit. Here are a couple pieces of advice that might
be useful:

(1) Remember you can return multiple values through throw.
So, for example, you can do:

(defvar *magic-token* (list '*magic-token*))

(defun error-throw (value) (throw 'error *magic-token* value))

(defun call-function-with-special-handling (normal-function
handler-function)
(let ((values (multiple-value-list
(catch 'error (funcall normal-function)))))
(if (eq (first values) *magic-token*)
(funcall handler-function (second values))
(values-list values)))) ;just return normally

If you don't like the consing of the multiple-value-list,
you can rewrite this as:

(defun call-function-with-special-handling (normal-function
handler-function)
(flet ((maybe-handle (&rest values)
(declare (dynamic-extent values))
(if (eq (first values) *magic-token*)
(funcall handler-function (second values))
(values-list values))))
(declare (dynamic-extent #'maybe-handle))
(multiple-value-call #'maybe-handle
(catch 'error (funcall normal-function)))))

I agree this is pretty clumsy, though. I offer it only to show
you can make a solution this way. The normal idiom most seasoned
programmers ultimately come to is shaped like this:

(2) The `catch of return' idiom:

(defun call-function-with-special-handling (normal-function
handler-function)
(block normal
(funcall handler-function
(catch 'error
(return-from normal
(funcall normal-function))))))

(defun error-throw (value) (throw 'error value))

Note that in this case, the error tag is only ever reached
in the case that the error was signalled, and not otherwise.

Btw, I'm not sure how you meant UNWIND-PROTECT to come into play at
all. If you give me a hint of what you're trying to protect, maybe
I can offer an example for that, too.

Or am I misunderstanding the question completely?

Barry Margolin

unread,
Jul 8, 1997, 3:00:00 AM7/8/97
to

In article <5ptmur$n...@news.u-bordeaux.fr>,

Laurent Peron <pe...@emi.u-bordeaux.fr> wrote:
>I would need some help with CATCH / THROW / UNWIND-PROTECT forms in
>Ansi Common Lisp.
>My idea would be to simulate classical languages exception handling
>mechanisms, ie execute some code because a given exception has been
>raised. eg (informal, stupid and ugly syntax, yes I know) :

You do realize that Common Lisp has a powerful exception handling system of
its own, don't you? It encompasses the features of most "classical"
exception handling mechanisms.

>What I would like to have is to execute some code if THROW raised
>an exception, and to write it in a clean and readable way.
>It seems that currently I have to make THROW return a
>special value, and test this value outside CATCH, ie
>
> (if (eql <special-value> (catch 'error <form to try>))
> (<form to execute because throw has sent the special value>))
>
>where the THROW would be
>
> (throw 'error <special-value>)
>
>Furthermore, I need to be sure that the <form to try> will
>never evaluate to my <special-value>.

That's one way to do it. An easy way to ensure that <form to try> never
evaluates to your special value is to cons a unique object on the fly.
E.g.

(let* ((special-value (cons nil nil)))
(if (eq special-value (catch 'error <form to try>))
...))

Another common way to do this is by using a flag variable:

(let ((form-aborted t)
value)
(catch 'error
(setq value <form to try>)
(setq form-aborted nil))
(if form-aborted
<abort form>
value))

The latter form is easier to use in macros, because it doesn't require that
the THROW form know about the special value, so it can be wrapped around
any existing code that already uses CATCH/THROW.

--
Barry Margolin, bar...@bbnplanet.com
BBN Corporation, Cambridge, MA
Support the anti-spam movement; see <http://www.cauce.org/>

Kent M Pitman

unread,
Jul 8, 1997, 3:00:00 AM7/8/97
to

In article <5ptscu$a...@tools.bbnplanet.com> Barry Margolin <bar...@bbnplanet.com> writes:

> Another common way to do this is by using a flag variable:
>
> (let ((form-aborted t)
> value)
> (catch 'error
> (setq value <form to try>)
> (setq form-aborted nil))
> (if form-aborted
> <abort form>
> value))
>
> The latter form is easier to use in macros, because it doesn't require that
> the THROW form know about the special value, so it can be wrapped around
> any existing code that already uses CATCH/THROW.

Right. In fact, since UNWIND-PROTECT was also mentioned, I should add that
there's a similar idiom (often something people wrap macrology
around to give it a nicer interface) involving unwind-protect, for when the
unwind needs to know if the form finished normally:

(let ((finished-p nil))
(unwind-protect (progn ... stuff ... (setq finished-p t))
... unwinds that access finished-p ...))

NOTE HOWEVER that in both this case and in Barry's case above,
finished-p=NIL (or form-aborted=T) is no guarantee that the
"... stuff ..." (or <form to try>) didn't finish,
it's only a guarantee that the form containing it
didn't finish. If, for example, the execution got as far as
(progn ... stuff ... (setq finished-p t))
^here
or even

(progn ... stuff ... (setq finished-p t ))
^here

if some sort of asynchronous interrupt happens (possible in some but not
all implementations), finished-p will still be NIL. So be careful how you
reason about this flag. It is ALMOST ALWAYS better to simply write:

(let ((old-value (figure-out-some-state)))
(unwind-protect (...whatever...)
(restore-state old-value)))

than what would seem cleverer with:

(let ((aborted t))
(unwind-protect (progn (push-some-state) (...whatever...)
(pop-some-state)
(setq aborted nil))
(when aborted (pop-some-state))))

because the aborted doesn't tell you as much as you might think.

Laurent Peron

unread,
Jul 10, 1997, 3:00:00 AM7/10/97
to

Barry Margolin (bar...@bbnplanet.com) wrote:
: You do realize that Common Lisp has a powerful exception handling system of

: its own, don't you? It encompasses the features of most "classical"
: exception handling mechanisms.

Probably it does, but I am a bit perplexed by this new huge language I
have just started learning. It is not easy to make "the good usage" of
a given form, macro, function, etc. This is why I asked here for
idioms related to exception handling.

At this point, being able to simulate what I am used to do with the
exception mechanisms I already know (ML, C, C++), seems enough to
me. And, since no one told me "peuh, this is not how to do this in CL,
you fool !", I believe what I am doing is the right way.


Thank you for your help.

Best Regards,

Laurent Peron

unread,
Jul 10, 1997, 3:00:00 AM7/10/97
to

Kent M Pitman (pit...@world.std.com) wrote:
: I had difficulty understanding the question you're asking so am not

: sure I can answer it directly. It seems to relate to a concern about
: distinguishing whether the the exit from the CATCH is a normal exit
: or the magic exit.

You got it. Sorry for my bad English.

: Here are a couple pieces of advice that might
: be useful:

: (1) Remember you can return multiple values through throw.
: So, for example, you can do:

[...]

: Btw, I'm not sure how you meant UNWIND-PROTECT to come into play at


: all. If you give me a hint of what you're trying to protect, maybe
: I can offer an example for that, too.

No, sorry, I am not trying to protect anything, but as I am just
a beginner, I thought that maybe UNWIND-PROTECT may have been
used in some idiom. I had forgotten the block structure, which does
the good work.

: Or am I misunderstanding the question completely?

No, it's perfect. Thank you very much.
Your solutions do exactly what I wanted to do.

Martin Rodgers

unread,
Jul 10, 1997, 3:00:00 AM7/10/97
to

With a mighty <5q24b2$p...@news.u-bordeaux.fr>,
pe...@emi.u-bordeaux.fr uttered these wise words...

> Probably it does, but I am a bit perplexed by this new huge language I
> have just started learning. It is not easy to make "the good usage" of
> a given form, macro, function, etc. This is why I asked here for
> idioms related to exception handling.

I too was perplexed by the size of Common Lisp, but I had the good
fortunate to learn a small subset first, from the 2nd ed of Winston
and Horn's Lisp tutorial. The 3rd edition uses a much larger subset,
but I believe it should still be "small" enough for a beginner. I
still consider myself to be a beginner, BTW.

Patrick H. Winston and Berthold K. P. Horn. "LISP", 3rd edition.
Addison-Wesley (Reading, MA), 1989. 611 pages. ISBN 0-201-08319-1



> At this point, being able to simulate what I am used to do with the
> exception mechanisms I already know (ML, C, C++), seems enough to
> me. And, since no one told me "peuh, this is not how to do this in CL,
> you fool !", I believe what I am doing is the right way.

A great way to learn Common Lisp is to simply dive in and start
swimming. It's best to start in the shallow end, but it's hard to know
where that is, without a good tutorial to show you. There are things
in CL that only make real sense once you _use them_. A lot of
experience helped design this Lisp dialect, and a beginner can't be
expected to appreciate it all immediate.

However, it is possible, esp if you start in the shallow end, and only
go deeper as your confidence grows. Like swimming, it can be a lot of
fun. It certainly was for me, and it still is.
--
<URL:http://www.wildcard.demon.co.uk/> You can never browse enough
Please note: my email address is gubbish
Will write Lisp code for food

Erik Naggum

unread,
Jul 11, 1997, 3:00:00 AM7/11/97
to

* Laurent Peron

| Probably it does, but I am a bit perplexed by this new huge language I
| have just started learning. It is not easy to make "the good usage" of a
| given form, macro, function, etc.

hm. this was how I felt when I had to use C++, but not when I was learning
Common Lisp. I sympathize strongly with the feeling that everything you do
could have been done better, but I think there is a difference between CL
and C++: I was actually afraid that what I did would break something
because I didn't do it the Right Way. in C++, that's exactly what happens,
and many programmers spend a large amount of time frustrated because of it.
in CL, it only wastes resources, or you get thrown into the debugger, which
should be pretty reasonable choices. (curiously, this fear of breaking
things seems to be the main threshold many people have towards computers in
the first place. I was sort of ashamed to realize that I experienced when
I had to deal with C++. but now I know it's because the computers people
fear and the languages programmers fear are just plain badly designed!)

perhaps indicative of the sorry state of the world, I have talked to C++
programmers who exhibit a fear of other languages for exactly this reason:
they think that every new language must be as hard to learn to do right as
the randomness of C++. I haven't seen this fear in other languages, not
even C. I have also seen a lot of C++ code that exhibit this fear, in that
the programmers sort of double check that things went well, especially
after the code has gained some age.

#\Erik
--
if DUI is "Driving Under the Influence"
then GUI must be "Graphics Under the Influence"

Erik Naggum

unread,
Jul 12, 1997, 3:00:00 AM7/12/97
to

* Kent M. Pitman
| Right. In fact, since UNWIND-PROTECT was also mentioned, ...
| It is ALMOST ALWAYS better to simply write:
|
| (let ((old-value (figure-out-some-state)))
| (unwind-protect (...whatever...)
| (restore-state old-value)))

sorry for butchering a fine article, but I have discovered something I
found to be quite hard to do in a reasonable manner. I first saw it in
Emacs Lisp, which can become quite seriously confused if you interrupt it
in the middle of some cleanup-forms that take non-zero time to complete.
Common Lisp the Standard reads:

# `unwind-protect' evaluates protected-form and guarantees that
# cleanup-forms are executed before `unwind-protect' exits, whether it
# terminates normally or is aborted by a control transfer of some kind.
# `unwind-protect' is intended to be used to make sure that certain side
# effects take place after the evaluation of protected-form.

if some interrupt can occur at any time in the protected-forms, it stands
to reason that it might as well occur in the cleanup-forms, especially if
it's a user hitting an interrupt key repeatedly. in Emacs, I experimented
with delaying quits (Emacs' name for handling a user's C-g) until after the
cleanup-forms had terminated, but it had the nasty side effect of making
Emacs uninterruptible in several cases.

so, how strong is the wording "guarantees that cleanup-forms are executed
before `unwind-protect' exits", considering that it is left open _how_ the
`unwind-protect' is exited, presuming that it could also be exited
non-locally?

under Unix, good programmers have become used to wrap critical code in a
whole lot of silly tests for error returns form everything under the sun,
but they still need a way to guarantee that a sequence of operations has
been completed before some other operation is started, and they have the
ability to belay all signals during critical regions. even though this is
Unix-centric, I imagine there must have been a way to obtain a similar
guarantee of completed sequences of operations even in the absence of an
ill-designed error return policy like Unix'. (I don't know much about
real-time programming, but it appears to me that such a question would have
had to be asked and answered in real-time systems.)

Barry Margolin

unread,
Jul 12, 1997, 3:00:00 AM7/12/97
to

In article <30776889...@naggum.no>, Erik Naggum <er...@naggum.no> wrote:
>so, how strong is the wording "guarantees that cleanup-forms are executed
>before `unwind-protect' exits", considering that it is left open _how_ the
>`unwind-protect' is exited, presuming that it could also be exited
>non-locally?

I don't have a copy of the standard handy (I generally use the HyperSpec,
but www.harlequin.com seems to be unreachable at the moment). But CLtL
says:

As a general rule, unwind-protect guarantees to execute the
cleanup-forms before exiting, whether it terminates normally or is
aborted by a throw of some kind. (If, however, an exit occurs during
execution of the cleanup-forms, no special action is taken. The
cleanup-forms of an unwind-protect are not protected by that
unwind-protect, though they may be protected if that unwind-protect
occurs within the protected form of another unwind-protect.)

As hinted above, you can use nested UNWIND-PROTECTs to protect some forms
more, e.g.

(unwind-protect ...
(unwind-protect (progn <important stuff>)
(unwind-protect (progn <really important stuff>)
<really REALLY important stuff>)))

The user would have to hit C-g 4 times to get out of the really REALLY
important stuff. If something really mustn't be aborted, you still have to
disable asynchronous aborts, and make sure that the code in that section
can't hang or loop.

I remember some long X3J13 debates about cleanup forms, but the thorny
issue turned out to be what happens to intervening UNWIND-PROTECTs and
other dynamic bindings if you abort out of an UNWIND-PROTECT. This was the
EXIT-EXTENT cleanup issue, and the result is described in the change-barred
section of the UNWIND-PROTECT description in CLtL2. I don't recall there
being any controversy over the answer to Erik's question, though -- it has
survived from CLtL1 intact.

Hrvoje Niksic

unread,
Jul 12, 1997, 3:00:00 AM7/12/97
to

Barry Margolin <bar...@bbnplanet.com> writes:

> As hinted above, you can use nested UNWIND-PROTECTs to protect some forms
> more, e.g.
>
> (unwind-protect ...
> (unwind-protect (progn <important stuff>)
> (unwind-protect (progn <really important stuff>)
> <really REALLY important stuff>)))

This doesn't answer Erik's point, since the <really REALLY important stuff>
can be interrupted just fine.

> The user would have to hit C-g 4 times to get out of the really
> REALLY important stuff.

Oh, this is easier than you think. The case Erik is talking about is
when, for instance, a user "sits" on C-g, and lets autorepeat do its
magic. In such cases you can get ten or more `C-g'-s in a fraction of
a second, without any hardship on the side of the unwitting user.

Now, you and I and Erik know that this is unwise, but it won't stop
users from doing exactly that, when they think that an Emacs operation
is "stuck". Emacs SHOULD have means of handling this situation
without further corruption.

Of course, one can wrap a `(let ((inhibit-quit t)) ...' around it, but
it doesn't strike me as a good general solution.

--
Hrvoje Niksic <hni...@srce.hr> | Student at FER Zagreb, Croatia
--------------------------------+--------------------------------
Thou Who might be our Father Who perhaps may be in Heaven...
-- Roger Zelazny

Erik Naggum

unread,
Jul 13, 1997, 3:00:00 AM7/13/97
to

* Hrvoje Niksic

| Emacs SHOULD have means of handling this situation without further
| corruption.

the situation I mention comes from Emacs, where user interrupts are very
easy, but the posed problem was intended to be more general. Kent Pitman
writes that interrupts may occur at any random point in the execution of
code, and suggests ways to ensure that the cleanup-forms are properly
executed, but there is no similar guarantee that they will be executed at
all, since there migt be another interrupt just before they are executed.
(I'm using "evaluate" to refer to Lisp forms, and "execute" to refer to
compiled machine code which _might_ be the interpreter, if any. I just
invented this distinction, so if it's useless, let me know.)

| Of course, one can wrap a `(let ((inhibit-quit t)) ...' around it, but it
| doesn't strike me as a good general solution.

in Emacs, this is possible, and it is indeed what I do when I really need
those cleanup forms, but there is still the window where establishing the
dynamic binding is susceptible to interrupts.

however, I'm asking for a Common Lisp solution to the same problem. Unix
has an answer (signal masks), but that's not at the right level, unless, of
course, the Lisp implementation uses maskable software signals to do the
job of exceptions and such, but it could become very messy, and very much
dependent on the implementation. ... which is precisely why I'm looking
for a clean interface to a complex solution.

Barry Margolin

unread,
Jul 13, 1997, 3:00:00 AM7/13/97
to

In article <30776889...@naggum.no>, Erik Naggum <er...@naggum.no> wrote:
>so, how strong is the wording "guarantees that cleanup-forms are executed
>before `unwind-protect' exits", considering that it is left open _how_ the
>`unwind-protect' is exited, presuming that it could also be exited
>non-locally?

The HyperSpec is back on-line. Here's what it says:

If a non-local exit occurs during execution of <cleanup-forms>, no
special action is taken. The <cleanup-forms> of UNWIND-PROTECT are not
protected by that UNWIND-PROTECT.

This is essentially equivalent to the CLtL text I copied in a previous
post, so it's very clear that there's no guarantee that all the cleanup
forms will be exited.

Basically, there's no way in portable CL code to guarantee that any
particular form will be executed. If the implementation has asynchronous
aborts, you need to use implementation-dependent extensions to block them
around critical code.

Kent M Pitman

unread,
Jul 14, 1997, 3:00:00 AM7/14/97
to

In article <30777743...@naggum.no> Erik Naggum <er...@naggum.no> writes:

In Maclisp (a Lisp for the PDP-10, late 70's, long before Macintoshes
or the Apple company saw the first light of day), unwind forms ran
with interrupts disabled.

I remember my first experiment with trying to write WITHOUT-INTERRUPTS.
I called it PI because it bound Program Interrupts and I thought
greek letters were cool; it went well with IOTA, which bound I/O
streams (the precursor to WITH-OPEN-FILE I ended up having to write when
someone complained I kept leaving open file streams that didn't get cleaned
up until the GC ran--sometimes a long time).

Anyway, my first attempt looked something like this (except that
backquote may not have existed, so it may have been uglier; I don't
remember the order of which came first):

(LET ((OLD (GENSYM)))
`(LET ((,OLD (STATUS NOINTERRUPT))) ;read the interrupt state
(UNWIND-PROTECT (PROGN (SSTATUS NOINTERRUPT T)
,@body)
(SSTATUS NOINTERRUPT ,OLD))))

and I couldn't figure out why it left interrupts disabled (heh) until
I found out about the fact that interrupts were bound off during
the unwind, and I realized that meant that they were restored to their
prior state after exit from the unwind.

I went to JonL, who was then my officemate but more importantly who
also maintained the compiler (and therefore the language). I explained
my problem, and he explained that unwinds ran with interrupts disabled.
Immediately I said, "aha, I know the answer" and went back and wrote

(DEFMACRO PI (&BODY FORMS) `(UNWIND-PROTECT NIL (PROGN ,@FORMS)))

Sure enough, that worked. And PI continued to operate that way
from then on out.

I frankly don't know for sure why or even when UNWIND-PROTECT changed
not to bind interrupts. I remember noticing that the change HAD
happened, and asking about it. My vague recollection is that someone
felt that writing code to manipulate interrupts needed to be done
with UNWIND-PROTECT and that consequently you couldn't have
UNWIND-PROTECT already doing that or it would make a mess (as had
happened for me with PI).

I'm not sure I really believe that answer, and in retrospect am sorry
I didn't make more fuss.

~~~~~
I have a vague recollection that Maclisp later changed so that you
could bind interrupts without UNWIND-PROTECT by just doing
(LET ((+INTERNAL-WITHOUT-INTERRUPTS T)) ...)
but the way this worked was a real hack. [Also, I may be wrong about
the variable, but it was something like that.] The value of this
variable was always garbage (it was either T or NIL, I don't recall,
but was on a pure page). When you SETQ'd or bound the variable, the
pure page trap handler would figure out you were trying to bind
interrupts and would read the old value and put it on the stack for
later restoration, then would call the setter. The variable value
would stay the same, but (STATUS NOINTERRUPT) would change value. I
think the point was to allow binding without involving UNWIND-PROTECT,
and without modifying the Maclisp stack format.

~~~~~

Anyway, back to Erik's question about how strong the guarantee is:
I'd say, not very strong. Surely not as strong as I'd wish.
Sorry about that. But then, in a Lisp that has interrupt control,
I guess you can shadow UNWIND-PROTECT with something better, even
still calling it UNWIND-PROTECT if you like.

They say Lisp is good for the very hardest of problems. I'd say
"politics of design" is right up there. And Lisp lives up to its
promise--you can even dig yourself out of bad design and escape
the ill consequences of us wrong-headed designers. Let's see C
compete with that!

Laurent Peron

unread,
Jul 15, 1997, 3:00:00 AM7/15/97
to

Erik Naggum (er...@naggum.no) wrote:
: * Laurent Peron

: | Probably it does, but I am a bit perplexed by this new huge language I
: | have just started learning. It is not easy to make "the good usage" of a
: | given form, macro, function, etc.

: hm. this was how I felt when I had to use C++, but not when I was learning
: Common Lisp. I sympathize strongly with the feeling that everything you do
: could have been done better, but I think there is a difference between CL
: and C++: I was actually afraid that what I did would break something
: because I didn't do it the Right Way.

[...]

I am not afraid of breaking something. I was thinking of CL idioms,
readability, and maintainability. I have not read much good CL
code yet, and I am not sure of what is the idiomatic way of
expressing what I want to express.

Regards,

Erik Naggum

unread,
Jul 15, 1997, 3:00:00 AM7/15/97
to

* Laurent Peron

| I was thinking of CL idioms, readability, and maintainability. I have
| not read much good CL code yet, and I am not sure of what is the
| idiomatic way of expressing what I want to express.

to become certain and to choose the right form by reflex will take a _very_
long time. however, idioms are not quite as necessary in Common Lisp as
they are in, e.g., C++ and Perl. well, I guess it depends on what you call
"idioms". is a functional programming style an "idiomatic" issue?

to take an example I have been idly wondering about. suppose you have an
Internet address as an integer. you want to produce a dotted-decimal form
of it. suppose we have the following code:

(defvar *network-byte-order*
(list (byte 8 0) (byte 8 8) (byte 8 16) (byte 8 24))
"Bytes of an address in network byte order.")

(defun dotted-decimal (address)
"Return the dotted-decimal form of the IP address ADDRESS."
(format nil "~{~D~^.~}"
(mapcar #'ldb *network-byte-order*
(list address address address address))))

now, is the `mapcar' form better written like this?

(mapcar (lambda (byte) (ldb byte address)) *network-byte-order*)

is a repeating list of the same elements better done with a function
`repeatedly', as in (defun repeatedly (&rest x) (nconc x x)):

(mapcar #'ldb *network-byte-order* (repeatedly address))

or is it better to use the straight-forward case:

(defun dotted-decimal (address)
"Return the dotted-decimal form of the IP address ADDRESS."
(format nil "~D.~D.~D.~D"
(ldb (byte 8 0) address)
(ldb (byte 8 8) address)
(ldb (byte 8 16) address)
(ldb (byte 8 24) address)))

I don't think such question have definite answers, but I'm not sure I'd
rate this is as _idiomatic_ usage of CL. actually, the languages where I
have heard "idiom" touted the most are Perl and C++, which sort of makes me
wonder about the value of "idiom" mainly as a cover over bad language
design, although I know this is unfair to good idioms. I'd suggest you
just write working code and keep an open mind to new suggestions when you
read code from others. above all, don't worry about it.

(incidentally, I think the Year 2000 problem is one of idiom and pattern
and failure to use the proper abstraction in dealing with input and output
of machine representations of dates. computers never should have had to
count the number of digits in a year representation. in my book, too much
idiom and pattern is much worse than abstraction when people are blindly
copying code.)

see Richard P. Gabriel: Patterns of Software for an excellent treatment of
the pros and cons of abstraction. I found it very refreshing and clear.

Kelly Murray

unread,
Jul 16, 1997, 3:00:00 AM7/16/97
to

#'Unwind-protect defines that control will always pass to the cleanup forms.
Once control is passed to the cleanup forms, they have no special status,
and so control can be passed out of them without all of the code being
executed. If this wasn't true, there would be no way to recover
from an error in a cleanup form (in Unix, it will dump core).

You can write cleanup code that is itself protected:

(unwind-protect
(update) ;; protected
;; cleanup
(unwind-protect
(risky-downdate) ;; protected cleanup
;; cleanup
(safe-downdate)
)
)

Common Lisp specifically says nothing about processes,
scheduling, interrupts, etc.
A 100% portable CL application can be assured that control
will always execute serially unless an error occurs.
So the form (progn (setf x 0) (setf x 1)) will always return 1
since no CL signal or error can occur between the two setf's.
An external interrupt (e.g. Control-C) is not an error,
and therefore, CL, has nothing to say about it.

In the real world on real computers on real operating systems,
external interrupts are occur, and how they are dealt with
is implementation-specific.


-Kelly Murray k...@franz.com




Aaron Gross

unread,
Jul 23, 1997, 3:00:00 AM7/23/97
to

Erik Naggum <er...@naggum.no> writes:

> see Richard P. Gabriel: Patterns of Software for an excellent treatment of
> the pros and cons of abstraction. I found it very refreshing and clear.

I skimmed over this recently, and I also thought it was refreshing,
although I'm still skeptical. I also read Paul Graham's "ANSI Common
Lisp" and skimmed through his "On Lisp", and I think there's a real
conflict between Gabriel's philosophy and Graham's.

One of Graham's main justifications for abstracting everything you can
get your hands on is that it makes the code shorter ("denser") and
consequently easier to understand. Gabriel acknowledges that the
non-abstract version is "harder to type" and "just plain longer"
(p. 29), but says that this problem "goes away with a well-designed
programming environment or doesn't matter at all when you consider the
habitability gains from using an easily understood program."

As an example (p. 28), Gabriel gives a typical call to the CL function
MISMATCH:

(mismatch sequence list :from-end t
:start1 20 :start2 40
:end1 120 :end2 140 :test #'baz)

compared with the nine-line pattern (which I don't want to type -- you
can write it yourself) that does the same thing with LET, REVERSE,
FLET, and LOOP. He implies that the nine-line version is easier to
understand: If you're familiar with the pattern, you can recognize it
immediately, and if you're not, you can understand it by reading
it. The MISMATCH example can be understood only by understanding the
function name and the meanings of the keywords.

I find this very unconvincing. I'm not an experienced Lisp programmer,
and I don't recognize the un-abstracted pattern immediately. I can
understand it by reading it, but it's easier for me to guess what the
MISMATCH example does, since the function is well-named and I'm
familiar with the conventions (patterns) involving :from-end, :start,
:end, and :test keywords. Failing this, I can always look at the
HyperSpec. I'm curious: Is there anyone out there other than Gabriel
who would prefer to see the nine lines of code instead of the call to
MISMATCH?

In an earlier example (p. 26), Gabriel considers the problem of
MAPCARing a function to a list and calculating the list's length at
the same time. The most abstract solution is also extremely
inefficient:

(let ((result (mapcar f list))
(len (length list)))
...)

Gabriel suggests (and then rejects) that one could replace this with
optimized code, and then the programming environment could display the
unoptimized version. This version "correctly respects the abstraction
of both operations...but it incorrectly overemphasizes abstraction
because the un-abstracted but common-pattern code is just fine:"

(let ((length 0)
(let ((result
(mapcar
#'(lambda (x)
(incf length)
(f x))
list)))
...))

Again, I'm not convinced. My guess is that Paul Graham would have
written a MAP-WITH-LENGTH utility to abstract out this pattern. Even
though the above pattern is instantly recognizable (even to a novice
like me), I'd rather see a one-line call to MAP-WITH-LENGTH, if for no
other reason than that it makes the code shorter. But maybe that's
just because I've been brainwashed by Paul Graham's books.

I know I didn't do justice to Gabriel's arguments here; I tried to
summarize without distorting, but I also omitted some other arguments
he makes concerning the over-use of abstraction. Still, I see Gabriel
and Graham as representing two opposing points of view, and I'm still
not convinced by Gabriel's somewhat heretical claims. I look forward
to hearing opinions of other Lisp programmers who are more experienced
than I am.

0 new messages