Let's take an example (any other expression that can signal any other
set of conditions could be considered equally).
(READ-FROM-STRING "ZORG:POPO") when no package ZORG exist.
We'd like to get a PACKAGE-DOES-NOT-EXIST-ERROR condition, with a
PACKAGE-DOES-NOT-EXIST-ERROR-PACKAGE-NAME slot, so if we want to
_handle_ this condition by making the package, we can. Then we could
retry.
And then, we'd like to get a PACKAGE-DOES-NOT-EXPORT-SYMBOL-ERROR with
a PACKAGE-DOES-NOT-EXPORT-SYMBOL-ERROR-SYMBOL slots, so if we want to
_handle_ this condition by making the symbol external, we can. Then
we could retry, and eventually successfully evaluate
(READ-FROM-STRING "ZORG:POPO").
I feel that's what the designers of the CONDITION classes had in mind
when they took the pain to define such a complex OO hierarchy of
condition.
Unfortunately, the Common Lisp specification doesn't specify enough
condition classes, and doesn't specify precisely enough what condition
the various CL functions SHOULD signal in the various error cases.
So each implementation can choose randomly what class of condition it
raises, and can choose randomly whether or in general NOT to add
meaningful (machine processable) slots to their implementation
specific condition subclasses, and therefore the condition system is
_unusable_ but for the more simplistic and lame handling: close down
everything and go home, which is what 99% of the lisp programs do.
Note that all the implementations (I have tested) _have_ the
information available to build their own kind of condition.
They use it to format the message string!
They all provide the type of "condition": "package inexistant" or
"symbol not exported" or "symbol inexistant", they all provide the
name of the package, and they all provide the name of the symbol!
Therefore there's no reason why they shouldn't all be standardized to
use the same FUTURE-CL:PACKAGE-DOES-NOT-EXIST-ERROR or
FUTURE-CL:PACKAGE-DOES-NOT-EXPORT-SYMBOL or
FUTURE-CL:SYMBOL-DOES-NOT-EXIST-ERROR with the same standardized slots to
hold the data.
But perhaps we could standardize another way to do it. Since it may
be laborious to make a census of all the condition classes and
attributes, perhaps we could have only one condition class, with a
property list of attributes that would have to be gathered at the
place of the error following some regular rules that would ensure that
all the data is collected with standard keys and can be processed by
the handlers. This would allow also to stack up the "different" error
levels: (PACKAGE-DOES-NOT-EXIST-ERROR PACKAGE-ERROR READER-ERROR) or
(SYMBOL-DOES-NOT-EXIST-ERROR SYMBOL-IS-NOT-EXPORTED PACKAGE-ERROR
READER-ERROR), dynamically, without a need for a previous
DEFINE-CONDITION whose name and slots would have to be standardized.
[pjb@thalassa pjb]$ clall '
(progn (handler-case (read-from-string "ZORG:POPO")
(error (err)
(let ((*print-readably* nil))
(finish-output)
(format t "CONDITION = ~S~
~2% ~A~
~%PACKAGE-ERROR ? ~:[NO~;YES~]~3%"
err err (subtypep (type-of err) (quote package-error))))))
(handler-case (read-from-string "COMMON-LISP-USER:POPO")
(error (err)
(let ((*print-readably* nil))
(finish-output)
(format t "CONDITION = ~S~
~2% ~A~
~%PACKAGE-ERROR ? ~:[NO~;YES~]~3%"
err err (subtypep (type-of err) (quote package-error)))))))'
------------------------------------------------------------------------
CLISP 2.39 (2006-07-16) (built 3364813332) (memory 3364813914)
CONDITION = #<SYSTEM::SIMPLE-PACKAGE-ERROR #x203C2C9E>
READ from #<INPUT STRING-INPUT-STREAM>: there is no package with name "ZORG"
PACKAGE-ERROR ? YES
CONDITION = #<SYSTEM::SIMPLE-PACKAGE-ERROR #x203C53EE>
READ from #<INPUT STRING-INPUT-STREAM>: #<PACKAGE COMMON-LISP-USER> has no external symbol with name "POPO"
PACKAGE-ERROR ? YES
(PROGN
(HANDLER-CASE (READ-FROM-STRING "ZORG:POPO")
(ERROR (ERR)
(LET ((*PRINT-READABLY* NIL)) (FINISH-OUTPUT)
(FORMAT T
"CONDITION = ~S~2% ~A~%PACKAGE-ERROR ? ~:[NO~;YES~]~3%" ERR ERR
(SUBTYPEP (TYPE-OF ERR) 'PACKAGE-ERROR)))))
(HANDLER-CASE (READ-FROM-STRING "COMMON-LISP-USER:POPO")
(ERROR (ERR)
(LET ((*PRINT-READABLY* NIL)) (FINISH-OUTPUT)
(FORMAT T
"CONDITION = ~S~2% ~A~%PACKAGE-ERROR ? ~:[NO~;YES~]~3%" ERR ERR
(SUBTYPEP (TYPE-OF ERR) 'PACKAGE-ERROR))))))
--> NIL
------------------------------------------------------------------------
SBCL 0.9.17
CONDITION = #<SB-KERNEL:READER-PACKAGE-ERROR {A654061}>
READER-ERROR at 9 (line 1, column 9) on #<SB-IMPL::STRING-INPUT-STREAM {A653D81}>:
package "ZORG" not found
PACKAGE-ERROR ? NO
CONDITION = #<SB-KERNEL:READER-PACKAGE-ERROR {A656271}>
READER-ERROR at 21 (line 1, column 21) on #<SB-IMPL::STRING-INPUT-STREAM {A655ED9}>:
Symbol "POPO" not found in the COMMON-LISP-USER package.
PACKAGE-ERROR ? NO
(PROGN
(HANDLER-CASE (READ-FROM-STRING "ZORG:POPO")
(ERROR (ERR)
(LET ((*PRINT-READABLY* NIL))
(FINISH-OUTPUT)
(FORMAT T
"CONDITION = ~S~2% ~A~%PACKAGE-ERROR ? ~:[NO~;YES~]~3%"
ERR ERR
(SUBTYPEP (TYPE-OF ERR) 'PACKAGE-ERROR)))))
(HANDLER-CASE (READ-FROM-STRING "COMMON-LISP-USER:POPO")
(ERROR (ERR)
(LET ((*PRINT-READABLY* NIL))
(FINISH-OUTPUT)
(FORMAT T
"CONDITION = ~S~2% ~A~%PACKAGE-ERROR ? ~:[NO~;YES~]~3%"
ERR ERR
(SUBTYPEP (TYPE-OF ERR) 'PACKAGE-ERROR))))))
--> NIL
------------------------------------------------------------------------
CMU Common Lisp 19c (19C)
CONDITION = #<LISP::READER-PACKAGE-ERROR {5802DB45}>
Reader error at 9 on #<String-Input Stream>:
package "ZORG" not found
PACKAGE-ERROR ? NO
CONDITION = #<LISP::READER-PACKAGE-ERROR {5803507D}>
Reader error at 21 on #<String-Input Stream>:
Symbol "POPO" not found in the COMMON-LISP-USER package.
PACKAGE-ERROR ? NO
(PROGN
(HANDLER-CASE (READ-FROM-STRING "ZORG:POPO")
(ERROR (ERR)
(LET ((*PRINT-READABLY* NIL))
(FINISH-OUTPUT)
(FORMAT T
"CONDITION = ~S~2% ~A~%PACKAGE-ERROR ? ~:[NO~;YES~]~3%"
ERR
ERR
(SUBTYPEP (TYPE-OF ERR) 'PACKAGE-ERROR)))))
(HANDLER-CASE (READ-FROM-STRING "COMMON-LISP-USER:POPO")
(ERROR (ERR)
(LET ((*PRINT-READABLY* NIL))
(FINISH-OUTPUT)
(FORMAT T
"CONDITION = ~S~2% ~A~%PACKAGE-ERROR ? ~:[NO~;YES~]~3%"
ERR
ERR
(SUBTYPEP (TYPE-OF ERR) 'PACKAGE-ERROR))))))
--> NIL
------------------------------------------------------------------------
GNU Common Lisp (GCL) GCL 2.6.7
CONDITION = #<CONDITIONS::INTERNAL-SIMPLE-STREAM-ERROR.0>
Error in RETURN-FROM [or a callee]: There is no package with the name ZORG.
PACKAGE-ERROR ? NO
CONDITION = #<CONDITIONS::INTERNAL-SIMPLE-STREAM-ERROR.1>
Error in RETURN-FROM [or a callee]: Cannot find the external symbol POPO in #<"COMMON-LISP-USER" package>.
PACKAGE-ERROR ? NO
(PROGN
(HANDLER-CASE (READ-FROM-STRING "ZORG:POPO")
(ERROR (ERR)
(LET ((*PRINT-READABLY* NIL))
(FINISH-OUTPUT)
(FORMAT T
"CONDITION = ~S~2% ~A~%PACKAGE-ERROR ? ~:[NO~;YES~]~3%"
ERR ERR (SUBTYPEP (TYPE-OF ERR) 'PACKAGE-ERROR)))))
(HANDLER-CASE (READ-FROM-STRING "COMMON-LISP-USER:POPO")
(ERROR (ERR)
(LET ((*PRINT-READABLY* NIL))
(FINISH-OUTPUT)
(FORMAT T
"CONDITION = ~S~2% ~A~%PACKAGE-ERROR ? ~:[NO~;YES~]~3%"
ERR ERR (SUBTYPEP (TYPE-OF ERR) 'PACKAGE-ERROR))))))
--> NIL
------------------------------------------------------------------------
ECL 0.9g
CONDITION = #<a SIMPLE-ERROR>
Cannot find the external symbol POPO in #<"ZORG" package>.
PACKAGE-ERROR ? NO
CONDITION = #<a SIMPLE-ERROR>
Cannot find the external symbol POPO in #<"COMMON-LISP-USER" package>.
PACKAGE-ERROR ? NO
(PROGN
(HANDLER-CASE (READ-FROM-STRING "ZORG:POPO")
(ERROR (ERR)
(LET ((*PRINT-READABLY* NIL))
(FINISH-OUTPUT)
(FORMAT T
"CONDITION = ~S~2% ~A~%PACKAGE-ERROR ? ~:[NO~;YES~]~3%"
ERR
ERR
(SUBTYPEP (TYPE-OF ERR) 'PACKAGE-ERROR)))))
(HANDLER-CASE (READ-FROM-STRING "COMMON-LISP-USER:POPO")
(ERROR (ERR)
(LET ((*PRINT-READABLY* NIL))
(FINISH-OUTPUT)
(FORMAT T
"CONDITION = ~S~2% ~A~%PACKAGE-ERROR ? ~:[NO~;YES~]~3%"
ERR
ERR
(SUBTYPEP (TYPE-OF ERR) 'PACKAGE-ERROR))))))
--> NIL
------------------------------------------------------------------------
International Allegro CL Free Express Edition 8.0 [Linux (x86)] (Jun 6, 2006 16:01)
CONDITION = #<READER-ERROR @ #x716dc212>
Package "ZORG" not found. [file position = 5]
PACKAGE-ERROR ? NO
CONDITION = #<READER-ERROR @ #x716dd88a>
Symbol "POPO" not found in the COMMON-LISP-USER package. [file position = 21]
PACKAGE-ERROR ? NO
(PROGN (HANDLER-CASE (READ-FROM-STRING "ZORG:POPO") (ERROR (ERR) (LET ((*PRINT-READABLY* NIL)) (FINISH-OUTPUT) (FORMAT T "CONDITION = ~S~2% ~A~%PACKAGE-ERROR ? ~:[NO~;YES~]~3%" ERR ERR (SUBTYPEP (TYPE-OF ERR) (QUOTE PACKAGE-ERROR)))))) (HANDLER-CASE (READ-FROM-STRING "COMMON-LISP-USER:POPO") (ERROR (ERR) (LET ((*PRINT-READABLY* NIL)) (FINISH-OUTPUT) (FORMAT T "CONDITION = ~S~2% ~A~%PACKAGE-ERROR ? ~:[NO~;YES~]~3%" ERR ERR (SUBTYPEP (TYPE-OF ERR) (QUOTE PACKAGE-ERROR)))))))
--> NIL
------------------------------------------------------------------------
[pjb@thalassa pjb]$
PS: Sorry for the SPOOMA.
--
__Pascal Bourguignon__ http://www.informatimago.com/
You're always typing.
Well, let's see you ignore my
sitting on your hands.
There is another problem, which is that the specification
1) _how_ those conditions are signaled: using SIGNAL, ERROR or CERROR.
2) what restarts should be available and what to do in case execution
is continued.
Regarding your example if you follow CLHS 2.2, you find
"10. An entire token has been accumulated. The object represented by
the token is returned as the result of the read operation, or an error
of type reader-error is signaled if the token is not of valid syntax."
So in principle one might argue that the error has to be a
reader-error. How is one to recover from a reader-error is not clear to
me.
Juanjo
Also, not everything is to blame on the implementations. The use of
conditions in ECL (due to the lack of restarts in many places) is far
from optimal.
Juanjo
Also, not everything is to blame on the specification. The use of
> I feel that's what the designers of the CONDITION classes had in mind
> when they took the pain to define such a complex OO hierarchy of
> condition.
>
>
>
> Unfortunately, the Common Lisp specification doesn't specify enough
> condition classes, and doesn't specify precisely enough what condition
> the various CL functions SHOULD signal in the various error cases.
>
>
> So each implementation can choose randomly what class of condition it
> raises, and can choose randomly whether or in general NOT to add
> meaningful (machine processable) slots to their implementation
> specific condition subclasses, and therefore the condition system is
> _unusable_ but for the more simplistic and lame handling: close down
> everything and go home, which is what 99% of the lisp programs do.
I think this is simply a case where the standardization committee simply
didn't have enough time and experience to flesh this out fully. I think
there was only one Lisp dialect that had a comparable condition system
at the time, Zetalisp. The CL Condition System was modeled after this,
but we didn't want to just rubber stamp its entire condition and restart
hierarchy.
So what we put in was a minimum set of useful conditions, and some
restarts for the easiest situations. The expectation was probably that
more would be added by de facto standardization, and it would later be
incorporated into the next revision of the standard.
The end result is that the Condition System is only minimally useful for
system-generated conditions. However, the architecture it provides
allows quite a bit of functionality for application-defined conditions.
--
Barry Margolin, bar...@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
> How can we really HANDLE exceptional conditions in Common Lisp?
> Unfortunately, the Common Lisp specification doesn't specify enough
> condition classes, and doesn't specify precisely enough what condition
> the various CL functions SHOULD signal in the various error cases.
Very interesting post.
(Parenthetically, I was able to do something useful once with just the
standard conditions. I wrote some code that automatically checks a
file out of a source code control system and tries again when an
attempt to overwrite the file fails.)
But I agree with your main idea that the coverage is much too small.
I think that a good place to start would be to write a proposal,
gather feedback from other smart users, and then implement the
proposal in the context of one or more free implementations.
-russ
Indeed.
In the meanwhile, the solution, if you want to handle precisely conditions
generated by functions in CL, is practically to re-implement them.
--
__Pascal_Bourguignon__ _ Software patents are endangering
() ASCII ribbon against html email (o_ the computer industry all around
/\ 1962:DO20I=1.100 //\ the world http://lpf.ai.mit.edu/
2001:my($f)=`fortune`; V_/ http://petition.eurolinux.org/