1

I want to print ,as described in the title, my whole function.

(DEFUN X () ...)

-> (DEFUN X () ...)

What do i need to write in "..." ?

Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
Jordan Zapf
  • 65
  • 1
  • 11

2 Answers2

5
#1=(defun x () (write '#1# :circle t))
Lars Brinkhoff
  • 13,542
  • 2
  • 28
  • 48
1

Define a my:defun that records the source

Lars's answer is a clever one, using circular structures to refer to the source within the source. Another option that might be a bit more useful for general introspection purposes is to define a special variable that provides access to the form being defined. Here's an initial, but not quite polished, version:

(defpackage #:introspective-common-lisp
  (:use "COMMON-LISP")
  (:shadow "DEFUN")
  (:nicknames #:icl))

(in-package #:icl)

(defvar *current-form* nil
  "The current form being evaluated (typically a definition form.")

(defmacro defun (&whole form name lambda-list &body body)
  "Like CL:DEFUN, except that within BODY, *CURRENT-FORM* is bound to
the defining ICL:DEFUN form."
  `(cl:defun ,name ,lambda-list 
     (let ((*current-form* ',form))
       ,@body)))

(defun x ()
  "A function that prints its source."
  (print *current-form*))

CL-USER> (in-package #:icl)
#<PACKAGE "INTROSPECTIVE-COMMON-LISP">
ICL> (x)

(DEFUN X
    NIL
  (PRINT *CURRENT-FORM*)) 
(DEFUN X
    NIL
  (PRINT *CURRENT-FORM*))

Remember that NIL and () are the same thing in Common Lisp, so (defun x () ...) is the same as (defun x nil ...). You could, of course, examine the value of *current-form* and decide to print () instead, but the point here is that you have access to the form, and you're now free to print it however you want (or do whatever else you want with it).

Better declaration handling

By parsing the body

With Common Lisp's macro facilities, this is actually a pretty easy thing to do, and I was able to cobble this one together in a very short time. However, there are some subtleties to be aware of. In this initial version, I expanded the custom icl:defun macro to

`(cl:defun ,name ,lambda-list 
   (let ((*current-form* ',form))
     ,@body)))

This will misplace declarations from body, though. This really needs to be something like:

`(cl:defun ,name ,lambda-list 
   ,@(util:body-declarations body)
   (let ((*current-form* ',form))
     ,@(util:body-forms body)))

There are packages out there that will parse a body into declarations/docstring and forms, and it's not too hard to roll your own, either.

By using &aux variables

You could also skip the let altogether, and add in a &aux variable to the cl:defun's lambda list, but to do that well, you'd need to check whether there's already an &aux keyword in the lambda list, because you wouldn't want to add a redundant one. This isn't too hard to do, but it does make our code a little bit more complicated:

(eval-when (:compile-toplevel :load-toplevel :execute)

(cl:defun with-aux-variable (lambda-list name &optional value)
  "Returns a new lambda list like LAMBDA-LIST (which should be an
ordinary lambda list), but with an NAME as an &aux variable with the
specified VALUE."
  (let ((tail (if (member '&aux lambda-list)
                  (list (list name value))
                  (list '&aux (list name value)))))
    (append lambda-list tail)))

) ; eval-when

(defmacro defun (&whole form name lambda-list &body body)
  "Like CL:DEFUN, except that within BODY, *CURRENT-FORM* is bound to
the defining ICL:DEFUN form."
  `(cl:defun ,name ,(with-aux-variable lambda-list 
                                       '*current-form*
                                       (list 'quote form))
     ,@body))
Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353