defmacro с defclass - PullRequest
       20

defmacro с defclass

7 голосов
/ 06 октября 2010

У меня есть класс в Common Lisp:

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

Я хотел создать макрос, который бы упростил эту избыточность ввода

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

В конце концов я хотел бы иметь (defclass my-cool-class () (make-slots '(foo bar baz)) и автоматически получать foo, bar и baz как слоты.

Но, когда я пошел, чтобы сделать macroexpand-1 make-slot, мальчик привет, я получил ошибки читателя.

Первым был «незаконный завершающий символ после двоеточия ...», а затем он продолжал идти.

SBCL 1.0.37.

edit: примеры синтаксически верны в системе, перед копированием я выполнил некоторое редактирование.


Шесть месяцев спустя -

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

Два с половиной года спустя.

Я обнаружил шестимесячный код в дикой природе в другом месте. Хотя я польщен, это также напоминает мне обновить это.

Если вам нравится эта идея, я храню этот код в: https://github.com/pnathan/defobject. Как и прежде, его целью является создание классов CLOS с минимумом повторяющейся типизации. Подобная система существует под названием DEFCLASS-STAR. Заинтересованным сторонам рекомендуется рассмотреть оба.

Ответы [ 4 ]

5 голосов
/ 06 октября 2010

Вы не можете поместить макросы в код, где хотите. Считать синтаксис конструкции в CLHS.

Например, вы не можете сделать:

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

DEFUN ожидает arglist, а не функцию, которая создает arglist.

Lisp расширяет макросы, где ожидаются формы Lisp. Там, где ожидаются другие списки (например, список слотов), Lisp не расширяет макрос.

Аналогично DEFCLASS ожидает список слотов, а не функцию, которая создает список слотов. Аналогично для списка слотов, DEFCLASS ожидает, что каждый слот будет либо именем, либо списком, описывающим слот.

Смотрите синтаксис DEFCLASS: http://www.lispworks.com/documentation/HyperSpec/Body/m_defcla.htm

Вы также не можете ставить запятые там, где хотите.

Вероятно, может помочь базовая книга на Лиспе. Прочитайте о синтаксисе Lisp.

:,foo

выше не имеет смысла.

Оператор запятой помещает элементы в списки с кавычками. Не помещает предметы в символы.

Если вы хотите создать символ, вам нужно вызвать INTERN или MAKE-SYMBOL.

Решение

Напишите макрос MY-DEFCLASS, который допускает более короткий синтаксис и расширяется до DEFCLASS. Уже есть макросы DEFCLASS *, которые делают что-то подобное в библиотеках.

3 голосов
/ 06 октября 2010

Я обычно использую что-то вроде этого

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

, а затем

(mydefclass foo (x y z))

Добавление некоторой логики для обработки потребности в пользовательских слотах очень просто (например, вы можете скопировать входное слово в расширении, когда поле является списком, а не символом)

2 голосов
/ 06 октября 2010

Как говорит Райнер, макросы раскрываются только тогда, когда вызов функции будет приемлемым.

То, что я сделал, чтобы ограничить базовую панель, которую мне нужно на самом деле набирать при определении слотов, - это иметь два макроса редактора, один для слотов с ридером и один для слотов с аксессором (у меня редко слоты с отдельными писатели, но если бы я это сделал, мне пришлось бы написать это от руки).

0 голосов
/ 07 октября 2010

Макросы расширяются рекурсивно сверху вниз. В вашем примере макрос defclass раскрывается первым - перед вашим макросом make-slot. Код, который раскрывает defclass, не ожидает нерасширенный макрос make-slot - он ожидает определения слота.

Как предположили другие, ошибки читателя вызваны тем, что `:,symbol не является допустимым Lisp. Но достаточно просто передать ключевое слово в макрос.

...