Any Hints on Saving an Invocation of a Macro/Function?
common-lisp, lisp
Solution
If you define your animal classes with a macro, then you can let the macro record the source code.
Example (works in LispWorks):
We can store the source code for example in a class allocated slot using an alist. Alternatively you could just use a simple global variable.
(defclass animal ()
((source :allocation :class :initform nil)))
Above has a slot which should point to an alist of class name and source code.
(defmacro def-animal-class (&whole code name feeding )
`(progn
(defclass ,name (animal) ())
(defmethod feed ((animal ,name)) ,@feeding)
(let ((item (assoc ',name
(slot-value (class-prototype (find-class 'animal))
'source))))
(if item
(setf (cdr item) ',code)
(setf (slot-value (class-prototype (find-class 'animal))
'source)
(list (cons ',name ',code)))))
',name))
What does the generated code look like?
CL-USER > (pprint (macroexpand-1 '(def-animal-class cat ((print "feeding a cat")))))
(PROGN
(DEFCLASS CAT (ANIMAL) NIL)
(DEFMETHOD FEED ((ANIMAL CAT)) (PRINT "feeding a cat"))
(LET ((ITEM (ASSOC 'CAT (SLOT-VALUE (CLASS-PROTOTYPE (FIND-CLASS 'ANIMAL))
'SOURCE))))
(IF ITEM
(SETF (CDR ITEM) '(DEF-ANIMAL-CLASS CAT ((PRINT "feeding a cat"))))
(SETF (SLOT-VALUE (CLASS-PROTOTYPE (FIND-CLASS 'ANIMAL)) 'SOURCE)
(LIST (CONS 'CAT '(DEF-ANIMAL-CLASS CAT ((PRINT "feeding a cat"))))))))
'CAT)
Using it:
CL-USER 75 > (def-animal-class cat ((print "feeding a cat some more")))
CAT
CL-USER 76 > (cdr (first (slot-value (class-prototype (find-class 'animal))
'source)))
(DEF-ANIMAL-CLASS CAT ((PRINT "feeding a cat some more")))
Thus the last source code gets recorded, whenever one uses the macro `DEF-ANIMAL-CLASS`. You can then for example write the code to a file:
(with-open-file (s "~/animals.sexp" :direction :output :if-exists :supersede)
(pprint (slot-value (class-prototype (find-class 'animal)) 'source) s))
A simple `READ` brings it back.
Problem
A while ago, for a little zoo-based example, I wrote a base class ANIMAL, some sub-classes CAT, MOUSE, etc. a generic method FEED taking an ANIMAL parameter and some methods specialised on each ANIMAL sub-type. After writing the second and third class, method pair I realised I was writing same thing over and over and decided to write a macro DEF-ANIMAL-SUBCLASS that expanded into a PROGN that defined the new sub-class and the appropriate method. I then realised that I had just given my users a way of defining their own ANIMAL sub-types, something they might find useful! However, while they might just do that in a running image, I didn't have a way of saving their new ANIMAL type so that, in the event that the image was restarted, any new ANIMAL type would be re-created for them (without them having to re-evaluate the macro). Is there a conventional way of doing this? Is it something that should not be done? Any hints would be gratefully received! Cheers, P