From: Marc Wachowitz
Subject: Re: Debugging mispellings?
Date: 
Message-ID: <6207ko$5ah$1@trumpet.uni-mannheim.de>
······@lavielle.com (Rainer Joswig) wrote:
> Hmm, Common Lisp code with Macros or Macros generating Macros
> is notoriously difficult to debug. Often you don't have an
> idea what generated your code or what your current stack frame
> corresponds to. [...] There is no real chance for source line debugging.

Clearly, a simple line-for-line correspondence between target code
and source code just isn't going to be as meaningful as in languages
without any macros - even C's rather primitive preprocessor can make
this quite complicated for a naive reader of the result. Yet, a Common
Lisp implementation could do quite a lot to cover most typical cases
(whether it's worth the implementation effort is another question).

Even though a macro can in principle generate code where fragments
don't correlate with the source in any way which could be described
very clearly (e.g. doing complex things with the string representation
of the involved symbols, and other hairy stuff with the CONS cells),
that's rare, and an implementation surely could associate information
about the origin and transformation-history of program source code
with the related objects, as long as there isn't too much semantically
meaningless reuse of identical CONS cells between otherwise unrelated
parts of the program (which is unlikely in normal development).

I expect that some heuristics for typical transformations done in macros
(e.g. building new symbols from existing symbols, list transformations
and backquote, GENSYM etc.), together with useful presentation styles
for such patterns, could deliver sufficiently understandable descriptions
of the target code's genesis - preferably via some kind of live hyper-
text which can "time-travel" through the various translation phases.

(As extreme measure, one could also record and partially replay an exact
trace of some or all calls to macro functions, but that's probably too
much stuff to keep around, and more than one would usually want to know ;-)

For some interesting ideas about hygienic macros, where such things
are relatively simple, look for SYNTAX-CASE in the Scheme repository
(this is a macro facility with the full power of the language - not
just the more restricted pattern language which has been described
in R4RS).

-- Marc Wachowitz <··@ipx2.rz.uni-mannheim.de>
From: Rainer Joswig
Subject: Re: Debugging mispellings?
Date: 
Message-ID: <joswig-ya023180001610970043390001@news.lavielle.com>
In article <············@trumpet.uni-mannheim.de>,
··@ipx2.rz.uni-mannheim.de (Marc Wachowitz) wrote:

> Clearly, a simple line-for-line correspondence between target code
> and source code just isn't going to be as meaningful as in languages
> without any macros - even C's rather primitive preprocessor can make
> this quite complicated for a naive reader of the result. Yet, a Common
> Lisp implementation could do quite a lot to cover most typical cases
> (whether it's worth the implementation effort is another question).

The mythical "sufficiently smart Lisp"? Haven't seen one yet
(well the Lispm comes close, but is much to complicated).

I have yet to find a Lisp being able to effectively debug
the perverse cases.



Have a look at the CL-HTTP code.

CLOS + Macros + Inlining + Local Macros.

> 
> Even though a macro can in principle generate code where fragments
> don't correlate with the source in any way which could be described
> very clearly (e.g. doing complex things with the string representation
> of the involved symbols, and other hairy stuff with the CONS cells),
> that's rare, and an implementation surely could associate information
> about the origin and transformation-history of program source code
> with the related objects, as long as there isn't too much semantically
> meaningless reuse of identical CONS cells between otherwise unrelated
> parts of the program (which is unlikely in normal development).


* (in-package :http)
#<The HTTP package, 2503/3130 internal, 278/525 external>

* (macroexpand '(define-standard-directory-export-types (:html :text :lisp
:image :audio :video :application :world)))

This short expression gives me:


(PROGN
 (PROGN
  (DEFINE-DIRECTORY-EXPORT-TYPE :DIRECTORY
                                (:HTML :TEXT :LISP :IMAGE :AUDIO :VIDEO
                                 :APPLICATION :WORLD))
  (DEFINE-DIRECTORY-EXPORT-TYPE :DIRECTORY-HIERARCHY
                                (:HTML :TEXT :LISP :IMAGE :AUDIO :VIDEO
                                 :APPLICATION :WORLD)
                                :DIRECTORIES
                                T)
  (DEFMETHOD EXPORT-URL
             :AROUND
             ((URL HTTP-PATH) (TRANSLATION (EQL :DIRECTORY)) &REST ARGS)
             (DESTRUCTURING-BIND
                 (&KEY RECURSIVE-P &ALLOW-OTHER-KEYS)
                 ARGS
               (IF RECURSIVE-P
                   (APPLY #'EXPORT-URL URL :DIRECTORY-HIERARCHY ARGS)
                   (CALL-NEXT-METHOD)))))
 (PROGN
  (DEFINE-DIRECTORY-EXPORT-TYPE :HTML-DIRECTORY (:HTML))
  (DEFINE-DIRECTORY-EXPORT-TYPE :HTML-DIRECTORY-HIERARCHY
                                (:HTML)
                                :DIRECTORIES
                                T)
  (DEFMETHOD EXPORT-URL
             :AROUND
             ((URL HTTP-PATH) (TRANSLATION (EQL :HTML-DIRECTORY)) &REST ARGS)
             (DESTRUCTURING-BIND
                 (&KEY RECURSIVE-P &ALLOW-OTHER-KEYS)
                 ARGS
               (IF RECURSIVE-P
                   (APPLY #'EXPORT-URL URL :HTML-DIRECTORY-HIERARCHY ARGS)
                   (CALL-NEXT-METHOD)))))
 (PROGN
  (DEFINE-DIRECTORY-EXPORT-TYPE :TEXT-DIRECTORY (:TEXT))
  (DEFINE-DIRECTORY-EXPORT-TYPE :TEXT-DIRECTORY-HIERARCHY
                                (:TEXT)
                                :DIRECTORIES
                                T)
  (DEFMETHOD EXPORT-URL
             :AROUND
             ((URL HTTP-PATH) (TRANSLATION (EQL :TEXT-DIRECTORY)) &REST ARGS)
             (DESTRUCTURING-BIND
                 (&KEY RECURSIVE-P &ALLOW-OTHER-KEYS)
                 ARGS
               (IF RECURSIVE-P
                   (APPLY #'EXPORT-URL URL :TEXT-DIRECTORY-HIERARCHY ARGS)
                   (CALL-NEXT-METHOD)))))
 (PROGN
  (DEFINE-DIRECTORY-EXPORT-TYPE :LISP-DIRECTORY (:LISP))
  (DEFINE-DIRECTORY-EXPORT-TYPE :LISP-DIRECTORY-HIERARCHY
                                (:LISP)
                                :DIRECTORIES
                                T)
  (DEFMETHOD EXPORT-URL
             :AROUND
             ((URL HTTP-PATH) (TRANSLATION (EQL :LISP-DIRECTORY)) &REST ARGS)
             (DESTRUCTURING-BIND
                 (&KEY RECURSIVE-P &ALLOW-OTHER-KEYS)
                 ARGS
               (IF RECURSIVE-P
                   (APPLY #'EXPORT-URL URL :LISP-DIRECTORY-HIERARCHY ARGS)
                   (CALL-NEXT-METHOD)))))
 (PROGN
  (DEFINE-DIRECTORY-EXPORT-TYPE :IMAGE-DIRECTORY (:IMAGE))
  (DEFINE-DIRECTORY-EXPORT-TYPE :IMAGE-DIRECTORY-HIERARCHY
                                (:IMAGE)
                                :DIRECTORIES
                                T)
  (DEFMETHOD EXPORT-URL
             :AROUND
             ((URL HTTP-PATH) (TRANSLATION (EQL :IMAGE-DIRECTORY)) &REST ARGS)
             (DESTRUCTURING-BIND
                 (&KEY RECURSIVE-P &ALLOW-OTHER-KEYS)
                 ARGS
               (IF RECURSIVE-P
                   (APPLY #'EXPORT-URL URL :IMAGE-DIRECTORY-HIERARCHY ARGS)
                   (CALL-NEXT-METHOD)))))
 (PROGN
  (DEFINE-DIRECTORY-EXPORT-TYPE :AUDIO-DIRECTORY (:AUDIO))
  (DEFINE-DIRECTORY-EXPORT-TYPE :AUDIO-DIRECTORY-HIERARCHY
                                (:AUDIO)
                                :DIRECTORIES
                                T)
  (DEFMETHOD EXPORT-URL
             :AROUND
             ((URL HTTP-PATH) (TRANSLATION (EQL :AUDIO-DIRECTORY)) &REST ARGS)
             (DESTRUCTURING-BIND
                 (&KEY RECURSIVE-P &ALLOW-OTHER-KEYS)
                 ARGS
               (IF RECURSIVE-P
                   (APPLY #'EXPORT-URL URL :AUDIO-DIRECTORY-HIERARCHY ARGS)
                   (CALL-NEXT-METHOD)))))
 (PROGN
  (DEFINE-DIRECTORY-EXPORT-TYPE :VIDEO-DIRECTORY (:VIDEO))
  (DEFINE-DIRECTORY-EXPORT-TYPE :VIDEO-DIRECTORY-HIERARCHY
                                (:VIDEO)
                                :DIRECTORIES
                                T)
  (DEFMETHOD EXPORT-URL
             :AROUND
             ((URL HTTP-PATH) (TRANSLATION (EQL :VIDEO-DIRECTORY)) &REST ARGS)
             (DESTRUCTURING-BIND
                 (&KEY RECURSIVE-P &ALLOW-OTHER-KEYS)
                 ARGS
               (IF RECURSIVE-P
                   (APPLY #'EXPORT-URL URL :VIDEO-DIRECTORY-HIERARCHY ARGS)
                   (CALL-NEXT-METHOD)))))
 (PROGN
  (DEFINE-DIRECTORY-EXPORT-TYPE :APPLICATION-DIRECTORY (:APPLICATION))
  (DEFINE-DIRECTORY-EXPORT-TYPE :APPLICATION-DIRECTORY-HIERARCHY
                                (:APPLICATION)
                                :DIRECTORIES
                                T)
  (DEFMETHOD EXPORT-URL
             :AROUND
             ((URL HTTP-PATH) (TRANSLATION (EQL :APPLICATION-DIRECTORY)) &REST
              ARGS)
             (DESTRUCTURING-BIND
                 (&KEY RECURSIVE-P &ALLOW-OTHER-KEYS)
                 ARGS
               (IF RECURSIVE-P
                   (APPLY #'EXPORT-URL
                          URL
                          :APPLICATION-DIRECTORY-HIERARCHY
                          ARGS)
                   (CALL-NEXT-METHOD)))))
 (PROGN
  (DEFINE-DIRECTORY-EXPORT-TYPE :WORLD-DIRECTORY (:WORLD))
  (DEFINE-DIRECTORY-EXPORT-TYPE :WORLD-DIRECTORY-HIERARCHY
                                (:WORLD)
                                :DIRECTORIES
                                T)
  (DEFMETHOD EXPORT-URL
             :AROUND
             ((URL HTTP-PATH) (TRANSLATION (EQL :WORLD-DIRECTORY)) &REST ARGS)
             (DESTRUCTURING-BIND
                 (&KEY RECURSIVE-P &ALLOW-OTHER-KEYS)
                 ARGS
               (IF RECURSIVE-P
                   (APPLY #'EXPORT-URL URL :WORLD-DIRECTORY-HIERARCHY ARGS)
                   (CALL-NEXT-METHOD))))))
T






Then expand the DEFINE-DIRECTORY-EXPORT-TYPE forms. One example:

* (macroexpand '(DEFINE-DIRECTORY-EXPORT-TYPE :WORLD-DIRECTORY (:WORLD)))


(PROGN
 (DEFMETHOD EXPORT-URL
            ((URL HTTP-PATH) (TRANSLATION (EQL :WORLD-DIRECTORY)) &REST ARGS)
            (DESTRUCTURING-BIND
                (&KEY PATHNAME RECACHE
                 (IMMEDIATE-EXPORT (NOT (EQL *AUTO-EXPORT* :ON-DEMAND)))
                 CHARACTER-SET &ALLOW-OTHER-KEYS)
                ARGS
              (COND
               (PATHNAME
                (SETF (TRANSLATED-PATHNAME URL)
                        (MAKE-DIRECTORY (TRANSLATED-PATHNAME PATHNAME))))
               (T
                (ERROR
                 "No PATHNAME was provided while exporting the URL, ~S,
with translation, ~S"
                 URL
                 TRANSLATION)))
              (SETF (TRANSLATION-METHOD URL)
                      :WORLD-DIRECTORY
                    (CHARACTER-SET URL)
                      CHARACTER-SET)
              (WHEN IMMEDIATE-EXPORT
                (LET ((URL-NAME-STRING (NAME-STRING URL)))
                  (%%MAP-EXPORT-DIRECTORY
                   (:URL URL :DATA-TYPES (:WORLD) :DIR-OPTIONS (:FILES)
                    :DIRECTORIES-P NIL)
                   (COND ((PATHNAME-DIRECTORY-P TRANSLATED) NIL)
                         ((AND (OR IMMEDIATE-EXPORT RECACHE)
                               (EXPORT-PATHNAME-P TRANSLATED))
                          (%EXPORT-PATHNAME TRANSLATED
                                            URL-NAME-STRING
                                            ARGS
                                            RECACHE))))))
              URL))
 (DEFMETHOD WRITE-DOCUMENT-HEADERS
            ((URL HTTP-PATH) (TRANSLATION (EQL :WORLD-DIRECTORY)) STREAM)
            (%WRITE-DOCUMENT-HEADERS-NO-PATHNAME URL :HTML STREAM NIL))
 (DEFMETHOD WRITE-DOCUMENT
            ((URL HTTP-PATH) (TRANSLATION (EQL :WORLD-DIRECTORY)) STREAM)
            (FLET ((INTERN-PATH-URL (PATH URL)
                     (INTERN-PATHNAME-AS-URL-INFERIOR PATH
                                                      URL
                                                      :IF-DOES-NOT-EXIST
                                                      :CREATE))
                   (ANCHOR-TEXT (URL PATHNAME DIRECTORY-FILE-P)
                     (COND (DIRECTORY-FILE-P NIL)
                           (T
                            (WITH-VALUE-CACHED (URL :DIRECTORY-STRING)
                                               (LET ((NAME (OBJECT URL))
                                                     (TYPE
                                                      (PATHNAME-TYPE PATHNAME))
                                                     (VERSION
                                                      (PATHNAME-VERSION
                                                       PATHNAME)))
                                                 (DECLARE
                                                  (DYNAMIC-EXTENT NAME))
                                                 (TYPECASE VERSION
                                                   ((OR KEYWORD NULL)
                                                    (CONCATENATE 'STRING
                                                                 NAME
                                                                 "."
                                                                 TYPE))
                                                   (T
                                                    (CONCATENATE 'STRING
                                                                 NAME
                                                                 "."
                                                                 TYPE
                                                                 "."
                                                                
(WRITE-TO-STRING
                                                                  VERSION
                                                                  :BASE
                                                                  10
                                                                  :ESCAPE
                                                                  NIL)))))))))
                   (INCLUSION-PREDICATE (PATHNAME)
                     (%MAKE-DATA-TYPE-PATHNAME-PREDICATE PATHNAME
                                                         (:WORLD)
                                                         NIL)))
              (DECLARE
               (DYNAMIC-EXTENT #'INTERN-PATH-URL #'ANCHOR-TEXT
                #'INCLUSION-PREDICATE))
              (WITH-CONDITIONAL-GET-RESPONSE
               (STREAM :HTML :EXPIRES (EXPIRATION-UNIVERSAL-TIME URL) :LOCATION
                URL :CACHE-CONTROL (RESPONSE-CACHE-CONTROL-DIRECTIVES URL)
                :CONTENT-LANGUAGE (LANGUAGES URL))
               (WRITE-DIRECTORY-LISTING URL
                                        STREAM
                                        #'INCLUSION-PREDICATE
                                        #'INTERN-PATH-URL
                                        #'ANCHOR-TEXT
                                        #'PATH-DIRECTORY-STRING
                                        NIL))))
 (DEFMETHOD %UNEXPORT-DIRECTORY
            ((URL HTTP-PATH) (TRANSLATION (EQL :WORLD-DIRECTORY)))
            (%%MAP-EXPORT-DIRECTORY
             (:URL URL :DATA-TYPES (:WORLD) :DIR-OPTIONS (:FILES)
              :DIRECTORIES-P NIL)
             (COND ((PATHNAME-DIRECTORY-P TRANSLATED) NIL)
                   ((EXPORT-PATHNAME-P TRANSLATED)
                    (UNEXPORT-PATHNAME-AS-URL-INFERIOR PATH URL NIL))))
            (%UNREGISTER-DIRECTORY-EXPORT-TYPE-MIME-MAJOR-TYPE
             :WORLD-DIRECTORY)
            (%UNREGISTER-DIRECTORY-EXPORT-TYPE :WORLD-DIRECTORY NIL)
            URL)
 (PROGN
  (DEFMETHOD EXPORT-DIRECTORY-PATHNAME-P
             ((PATHNAME PATHNAME) (EXPORT-TYPE (EQL :WORLD-DIRECTORY)))
             (%MAKE-DATA-TYPE-PATHNAME-PREDICATE PATHNAME (:WORLD) NIL))
  (DEFMETHOD DIRECTORY-TYPE-EXPORTS-PATHNAME-EXPORT-TYPE-P
             ((DIRECTORY-TYPE (EQL :WORLD-DIRECTORY)) (EXPORT-TYPE SYMBOL))
             (LET ((MAJOR-TYPE
                    (MIME-CONTENT-TYPE-MAJOR-TYPE
                     (PRIMARY-PATHNAME-EXTENSION-FOR-EXPORT-TYPE EXPORT-TYPE))))
               (MEMBER MAJOR-TYPE '(:WORLD) :TEST #'EQ))))
 (%REGISTER-DIRECTORY-EXPORT-TYPE-MIME-MAJOR-TYPE :WORLD-DIRECTORY '(:WORLD))
 (%REGISTER-DIRECTORY-EXPORT-TYPE :WORLD-DIRECTORY 'NIL)
 :WORLD-DIRECTORY)
T

You can expand this for some while. Now imagine in one of those
methods you have an error. Which of those macro transformations
did introduce the error (if any)?

I'm not sure how many people on this planet can debug that.
You better read "On Lisp" before trying.

-- 
http://www.lavielle.com/~joswig/