2010-10-06 6 views
7

Ho una classe in Common Lisp:defmacro con defclass

(defclass my-cool-class() 
    ((variable1 
    :initarg :variable1 
    :accessor variable1 
    :initform (error "Must supply value to variable1")) 
    (variable2 
    :initarg :variable2 
    :accessor variable2 
    :initform (error "Must supply value to variable2")) 

ho voluto creare una macro che semplificherebbe questa ridondanza di digitare

(defmacro make-slot (slot-name) 
    `(slot-name 
    :initarg :,slot-name 
    :accessor :,slot-name 
    :initform (error "Must supply value"))) 

Alla fine mi piacerebbe avere (defclass my-cool-class() (make-slot '(foo bar baz)) e ottenere foo, bar e baz come slot automagically.

Ma, quando sono andato a fare un macroexpand-1 di make-slot, ragazzo howdy ho avuto errori di lettura.

Il primo era "carattere di chiusura illegale dopo un colon ..." e poi ha continuato.

SBCL 1.0.37.

modifica: gli esempi sono sintatticamente corretti sul sistema, ho fatto qualche redazione prima di copiare.


sei mesi più tardi -

anni
(defun build-var (classname var) 
    (list var 
     :initform nil 
     :accessor (intern (concatenate 'string (string classname) "-" 
             (string var))) 
     :initarg (intern (string var) :keyword))) 

(defun build-varlist (classname varlist) 
    (loop for var in varlist 
     collect (build-var classname var))) 


(defmacro defobject (name &rest varlist) 
    "Defines a class with a set of behavior. 
    Variables are accessed by name-varname. 

    (defobject classname v1 v2 v3) 
    " 
    `(defclass ,name() 
    ,(build-varlist name varlist))): 

Due anni e mezzo più tardi.

Ho scoperto il codice di sei mesi in natura altrove. Mentre sono lusingato, mi ricorda anche di aggiornare questo.

Se ti piace questa idea, tengo questo codice a: https://github.com/pnathan/defobject. Come prima, il suo obiettivo è quello di produrre classi CLOS con il minimo di digitazione ripetitiva. Esiste un sistema simile chiamato DEFCLASS-STAR. Le parti interessate sono invitate a rivedere entrambi.

risposta

5

Non è possibile inserire macro nel codice dove si desidera. Legge la sintassi per un costrutto in CLHS.

Per esempio non si può fare:

(defun foo (make-arg-list 'a 'b) a b) 

Defun si aspetta un arglist e non una funzione che crea un arglist.

Lisp espande le macro, dove sono previste forme Lisp. Dove sono previsti altri elenchi (ad esempio un elenco di slot), Lisp non espande macro.

DEFCLASS simile si aspetta un elenco di slot e non una funzione che crea un elenco di slot. Simile per la lista di slot, DEFCLASS si aspetta che ogni slot sia un nome o un elenco che descrive lo slot.

visualizzare la sintassi di DEFCLASS: http://www.lispworks.com/documentation/HyperSpec/Body/m_defcla.htm

Non si può anche mettere le virgole in cui si desidera.

Probabilmente un libro Lisp di base potrebbe essere d'aiuto. Leggi la sintassi Lisp.

:,foo 

non significativo.

L'operatore virgola inserisce gli elementi in elenchi di riepiloghi. Non mette oggetti in simboli.

Se si desidera creare un simbolo, è necessario chiamare INTERN o MAKE-SYMBOL.

Soluzione

scrivere una macro MY-DEFCLASS che permette una sintassi più breve e si espande in DEFCLASS. Esistono già macro DEFCLASS * che fanno qualcosa di simile nelle librerie.

+0

Hm. Ho pensato - erroneamente evidentemente - che una macro si sarebbe espansa in modo sostitutivo; quindi, defclass vedrebbe la lista di slot. –

+0

@Paul Nathan, come dovrebbe Lisp decidere che MAKE-SLOT è una macro e non il nome di uno slot? La sintassi DEFCLASS è chiara, si aspetta un elenco di slot. Non si aspetta una funzione che valuti in una lista di slot. –

+0

Ah, immagino che il mio errore principale qui sia il mio modo di pensare su come le macro vengono espanse e quando. –

2

Come dice Rainer, le macro vengono espanse solo quando una chiamata di funzione è accettabile.

Quello che ho fatto per limitare il piatto della caldaia che devo veramente scrivere quando si definiscono gli slot, è di avere due macro di editor, una per gli slot con un lettore e una per gli slot con un accessor (raramente ho slot con scrittori separati, ma se lo facessi, dovrei scriverlo a mano).

3

Io di solito uso qualcosa di simile

(defmacro mydefclass (name fields) 
    `(defclass ,name() 
    ,(let ((res nil)) 
     (dolist (f fields) 
      (let* ((fname (symbol-name f)) 
       (kw (intern fname :keyword))) 
      (push `(,f :accessor ,kw 
         :initarg ,kw 
         :initform (error 
            ,(format NIL "Must supply value to ~a" 
              fname))) 
        res))) 
     (nreverse res)))) 

e poi

(mydefclass foo (x y z)) 

Aggiunta di una certa logica per gestire la necessità di slot personalizzati è molto semplice (per esempio si potrebbe copiare l'ingresso testualmente nel espansione quando un campo è un elenco e non un simbolo)

0

Le macro vengono espanse in modo ricorsivo dall'alto verso il basso. Nel tuo esempio, la macro defclass viene espansa per prima, prima della macro make-slot. Il codice che si espande defclass non si aspetta una macro make-slot non espansa - è in attesa di una definizione di slot.

Come suggerito da altri, gli errori del lettore sono dovuti al fatto che `:,symbol non è un Lisp valido. Ma è abbastanza facile passare semplicemente una parola chiave nella macro in primo luogo.

+1

Interessante. Non ero a conoscenza del fatto che defclass fosse una macro. Penso che proverò il consiglio di Rainer e prenderò un libro di carta Lisp. –

+1

@Paul Nathan, tutti i DEFSOMETHING sono in genere macro. –

Problemi correlati