2010-10-06 8 views
7

tengo una clase en 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")) 

quería crear una macro que simplificaría esta redundancia de escribir

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

el tiempo me gustaría tener (defclass my-cool-class() (make-slots '(foo bar baz)) y obtener foo, bar y baz como slots automágicamente.

Pero, cuando fui a hacer un macroexpand-1 de make-slot, chico ¿cómo puedo obtener errores de lector?

El primero fue "carácter de terminación ilegal después de un colón ..." y luego continuó.

SBCL 1.0.37.

editar: los ejemplos son sintácticamente correctos en el sistema, hice algo de redacción antes de copiar.


Seis meses más tarde -

años
(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))): 

Dos años y medio más tarde.

Descubrí el código de seis meses en otro lugar. Aunque me siento halagado, también me recuerda actualizar esto.

Si te gusta esta idea, guardo este código guardado en: https://github.com/pnathan/defobject. Como antes, su objetivo es producir clases CLOS con el mínimo de tipeo repetitivo. Existe un sistema similar llamado DEFCLASS-STAR. Se recomienda a las partes interesadas que revisen ambas.

Respuesta

5

No puede poner macros en el código donde desee. Lea la sintaxis de un constructo en CLHS.

Por ejemplo, usted no puede hacer:

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

DEFUN espera un arglist y no una función que crea una arglist.

Lisp expande macros, donde se esperan formularios Lisp. Donde se esperan otras listas (por ejemplo, una lista de slots), Lisp no macroexpand.

Similar DEFCLASS espera una lista de ranuras y no una función que crea una lista de ranuras. Similar a la lista de ranuras, DEFCLASS espera que cada ranura sea un nombre o una lista que describa la ranura.

Consulte la sintaxis de DEFCLASS: http://www.lispworks.com/documentation/HyperSpec/Body/m_defcla.htm

No se puede también poner comas en las que desee.

Probabaly un libro básico de Lisp puede ayudar. Lea sobre la sintaxis de Lisp.

:,foo 

anterior no es significativo.

El operador de coma coloca los elementos en las listas de citas falsas. No pone elementos en símbolos.

Si desea crear un símbolo, debe llamar a INTERN o MAKE-SYMBOL.

Solución

escribir una macro-MI DEFCLASS que permite una sintaxis más corta y se expande en DEFCLASS. Ya hay macros DEFCLASS * que están haciendo algo así en las bibliotecas.

+0

Hm. Pensé, por error, evidentemente, que una macro se expandiría en forma de reemplazo; por lo tanto, defclass vería la lista de slots. –

+0

@Paul Nathan, ¿cómo debería decidir Lisp que MAKE-SLOT es una macro y no el nombre de una ranura? La sintaxis de DEFCLASS es clara, espera una lista de ranuras. No espera una función que se evalúa en una lista de ranuras. –

+0

Ah, supongo que mi error principal es pensar cómo se expanden las macros y cuándo. –

2

Como dice Rainer, las macros solo se amplían cuando una llamada a función sería aceptable.

Lo que he hecho para limitar la placa de la caldera que necesito escribir al momento de definir las ranuras, es tener dos macros de edición, una para las ranuras con un lector y otra para las ranuras con un accesorio (rara vez tengo ranuras con escritores independientes, pero si lo hiciera, tendría que escribirlo a mano).

3

normalmente uso algo como esto

(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)))) 

y luego

(mydefclass foo (x y z)) 

Agregando un poco de lógica para manejar la necesidad de ranuras personalizados es muy fácil (por ejemplo, usted podría copiar la entrada textualmente en el expansión cuando un campo es una lista y no un símbolo)

0

Las macros se expanden recursivamente de arriba hacia abajo. En su ejemplo, la macro defclass se expande primero, antes que su macro make-slot. El código que se expande defclass no espera una macro make-slot no expandida; está esperando una definición de ranura.

Según lo sugerido por otros, los errores del lector se deben a que `:,symbol no es válido Lisp. Pero, en primer lugar, es bastante fácil pasar una palabra clave a la macro.

+1

Interesante. No sabía que la deflagración en sí misma era una macro. Creo que seguiré el consejo de Rainer y obtendré un libro Lisp en papel. –

+1

@Paul Nathan, todos los DEFSOMETHING son generalmente macros. –

Cuestiones relacionadas