Как получить доступ к метаобъектам / слотам определения слотов? Почему `slot-value` может обращаться к слотам объектов, но не к слотам метаобъектов?

У меня проблема с доступом к слотам из определений слотов. Я могу проверять объекты классов, видеть их определения слотов и даже получать некоторую стандартную информацию об определениях слотов. Однако я не могу получить доступ к пользовательской информации об определении слотов.

Я уже некоторое время гуглял по этому поводу и в итоге прочитал спецификации CLOS & MOP, небольшую часть кулинарной книги по Lisp, некоторые концепции MOP и некоторые связанные вопросы по Stackru, которые не сильно помогли. Я даже прочитал часть реализаций SBCL, но безрезультатно.

Из частей, которые я смог собрать, я могу получить доступ ко многим слотам SLOT DEFINITION через некоторые функции, например, доступ к NAME слот SLOT DEFINITION с помощью CLOSER-MOP:SLOT-DEFINITION-NAME (что, безусловно, полезно), но я не могу сделать это для слотов, у которых нет одной из этих функций. Например, я не могу получить доступ к REFERENCES слот, который предоставляется MITO пакет при определении слотов в DEFCLASS,

Вот минимальный рабочий пример:

(load "~/quicklisp/setup.lisp")
;;;; I'll use MITO because its classes have a funny REFERENCES slot
(quicklisp:quickload :mito)
;;;; I find CLOSER-MOP functions easier to use than
;;;; implementation-specific functions
(quicklisp:quickload :closer-mop)


;;;; Creates a few dummy classes
(defclass example ()
  ((one-slot :col-type (:varchar 50)
             :accessor one-slot-accessor))
  (:metaclass mito:dao-table-class)
  (:documentation "An example class."))

(defclass another-example ()
  ((normal-slot :col-type (:varchar 50)
                :accessor normal-slot-accessor)
   (example-reference :references (example id)
                      :reader example-reference-accessor))
  (:metaclass mito:dao-table-class)
  (:documentation "Another example class which references `EXAMPLE' class."))


;;;; Now try to see what's inside those classes
(let* ((class (find-class 'another-example))
       (slots (closer-mop:class-direct-slots class))
       (slot-i-am-interested (second slots)))
  (inspect slot-i-am-interested) ; Oh, apparently we have a REFERENCES slot
  ;; Why can't SLOT-VALUE access the REFERENCES slot?
  (slot-value slot-i-am-interested 'references))

Обратите внимание, что я не обращаюсь ни к какой базе данных или чему-то подобному, хотя я использую MITO (Я не думаю, что я получил бы эту проблему, если бы я не использовал какой-либо пользовательский слот, как REFERENCES тот MITO обеспечивает). Это просто манипуляция CLOS/MOP.

Обычный вывод выглядит примерно так (точный вывод зависит от вашей реализации Common Lisp):

user@linuxstudio:~/dev/lisp/slot-question-so$ sbcl --script example.lisp
To load "mito":
  Load 1 ASDF system:
    mito
; Loading "mito"
....
To load "closer-mop":
  Load 1 ASDF system:
    closer-mop
; Loading "closer-mop"


The object is a STANDARD-OBJECT of type MITO.DAO.COLUMN:DAO-TABLE-COLUMN-CLASS.
0. SOURCE: #S(SB-C:DEFINITION-SOURCE-LOCATION
              :NAMESTRING "/home/user/dev/lisp/slot-question-so/example.lisp"
              :INDICES 163840)
1. NAME: EXAMPLE-REFERENCE
2. INITFORM: NIL
3. INITFUNCTION: NIL
4. INITARGS: (:EXAMPLE-REFERENCE)
5. %TYPE: T
6. %DOCUMENTATION: NIL
7. %CLASS: #<MITO.DAO.TABLE:DAO-TABLE-CLASS COMMON-LISP-USER::ANOTHER-EXAMPLE>
8. READERS: (EXAMPLE-REFERENCE-ACCESSOR)
9. WRITERS: NIL
10. ALLOCATION: :INSTANCE
11. ALLOCATION-CLASS: NIL
12. COL-TYPE: NIL
13. REFERENCES: (EXAMPLE ID)
14. PRIMARY-KEY: NIL
15. GHOST: NIL
16. INFLATE: "unbound"
17. DEFLATE: "unbound"
> q

Итак, по-видимому, у нас есть REFERENCES слот. Однако после INSPECT когда мы пытаемся SLOT-VALUE слот, мы получаем SLOT-MISSING ошибка (отображаются только первые строки обратного следа):

Unhandled SIMPLE-ERROR in thread #<SB-THREAD:THREAD "main thread" RUNNING
                                    {10005E85B3}>:
  When attempting to read the slot's value (slot-value), the slot REFERENCES is
  missing from the object
  #<MITO.DAO.COLUMN:DAO-TABLE-COLUMN-CLASS COMMON-LISP-USER::EXAMPLE-REFERENCE>.

Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {10005E85B3}>
0: (SB-DEBUG::DEBUGGER-DISABLED-HOOK #<SIMPLE-ERROR "~@<When attempting to ~A, the slot ~S is missing from the ~
          object ~S.~@:>" {1004B1B803}> #<unused argument> :QUIT T)
1: (SB-DEBUG::RUN-HOOK *INVOKE-DEBUGGER-HOOK* #<SIMPLE-ERROR "~@<When attempting to ~A, the slot ~S is missing from the ~
          object ~S.~@:>" {1004B1B803}>)
2: (INVOKE-DEBUGGER #<SIMPLE-ERROR "~@<When attempting to ~A, the slot ~S is missing from the ~
          object ~S.~@:>" {1004B1B803}>)
3: (ERROR "~@<When attempting to ~A, the slot ~S is missing from the ~
          object ~S.~@:>" "read the slot's value (slot-value)" REFERENCES #<MITO.DAO.COLUMN:DAO-TABLE-COLUMN-CLASS COMMON-LISP-USER::EXAMPLE-REFERENCE>)
4: ((:METHOD SLOT-MISSING (T T T T)) #<unused argument> #<MITO.DAO.COLUMN:DAO-TABLE-COLUMN-CLASS COMMON-LISP-USER::EXAMPLE-REFERENCE> REFERENCES SLOT-VALUE NIL) [fast-method]
5: ((LAMBDA (SB-PCL::OBJECT) :IN SB-PCL::SLOT-MISSING-INFO) #<MITO.DAO.COLUMN:DAO-TABLE-COLUMN-CLASS COMMON-LISP-USER::EXAMPLE-REFERENCE>)
6: ((LAMBDA NIL :IN "/home/user/dev/lisp/slot-question-so/example.lisp"))

Так как я могу получить доступ к "слоту" REFERENCES? Это действительно слот? Если нет, как я могу получить к нему доступ? Почему простой SLOT-VALUE не работает в этом случае?

Можете ли вы указать мне некоторую документацию по этой теме, чтобы предоставить больше информации, если я хочу больше узнать об этом?

я использую SBCL 1.4.5.debian как моя реализация LISP, если это может быть важно.

1 ответ

Решение

Имена слотов являются символами, поэтому пакет имеет значение при использовании SLOT-VALUE / WITH-SLOTS, В этом случае слоты ссылок кажутся именованными внутренним символом в пакете MITO.CLASS.COLUMN,

Инспектор не показывает имя пакета (потому что оно редко требуется), но вы можете видеть, что определение слота имеет тип MITO.DAO.COLUMN:DAO-TABLE-COLUMN-CLASSтак что вы можете использовать CLOSER-MOP:CLASS-SLOTS чтобы найти определение слота:

CL-USER> (closer-mop:class-slots (find-class 'MITO.DAO.COLUMN:DAO-TABLE-COLUMN-CLASS))
(#<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION SB-PCL::SOURCE>
 #<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION SB-PCL::NAME>
 #<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION SB-PCL::INITFORM>
 #<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION SB-PCL::INITFUNCTION>
 #<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION SB-PCL::INITARGS>
 #<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION SB-PCL::%TYPE>
 #<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION SB-PCL::%DOCUMENTATION>
 #<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION SB-PCL::%CLASS>
 #<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION SB-PCL::READERS>
 #<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION SB-PCL::WRITERS>
 #<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION SB-PCL::ALLOCATION>
 #<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION SB-PCL::ALLOCATION-CLASS>
 #<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION MITO.CLASS.COLUMN::COL-TYPE>
 #<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION MITO.CLASS.COLUMN::REFERENCES>   ;<-- HERE
 #<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION MITO.CLASS.COLUMN::PRIMARY-KEY>
 #<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION MITO.CLASS.COLUMN::GHOST>
 #<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION MITO.DAO.COLUMN::INFLATE>
 #<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION MITO.DAO.COLUMN::DEFLATE>)

В качестве альтернативы вы могли бы попробовать (apropos "references") чтобы увидеть все пакеты с таким интернированным символом:

CL-USER> (apropos "references")
REFERENCES
:HAS-EXTERNAL-REFERENCES-P (bound)
:REFERENCES (bound)
MITO.CLASS.COLUMN::REFERENCES  ;<-- HERE
MITO.CLASS.COLUMN:TABLE-COLUMN-REFERENCES (fbound)
MITO.CLASS.COLUMN:TABLE-COLUMN-REFERENCES-COLUMN (fbound)
...
Другие вопросы по тегам