2
votes

I'm having a problem acessing slots out of slot definitions. I can inspect class objects, see their slots definitions and even get some standard info about the slots definitions. However I can't access user-defined information about the slot definition.

I've already googled this for quite a while and ended up reading CLOS & MOP specifications, a bit of the Lisp Cookbook, about some MOP concepts, and some related questions on StackOverflow that didn't help much. I even readed a piece of SBCL's implementations, but to no avail.

From the pieces that I was able to put together, I can access many slots of a SLOT DEFINITION via some functions, e.g. access the NAME slot of the SLOT DEFINITION using CLOSER-MOP:SLOT-DEFINITION-NAME (which is certainly helpful), but I can't do so for slots that don't have one of these functions. For example, I can't access the REFERENCES slot which is provided by the MITO package when defining slots in a DEFCLASS.

Here's a minimal working example:

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

Note that I am NOT accessing any database or anything similar, although I'm using MITO (I don't think I would get this problem if I didn't use any custom slot, like the REFERENCES one that MITO provides). It's just plain CLOS/MOP manipulation.

The usual output is something like (the exact output depends on your Common Lisp implementation):

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

So, apparently, we do have a REFERENCES slot. However, after INSPECT, when we try to SLOT-VALUE the slot, we get an SLOT-MISSING error (showing only the first lines of the backtrace):

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

So how can I access the "slot" REFERENCES? Is that really a slot? If no, how can I access it? Why a simple SLOT-VALUE doesn't work in this case?

Can you point me to some documentation about this topic to provide more information in case I want to understand more about this?

I'm using SBCL 1.4.5.debian as my lisp implementation, if that may be important.

1
Remember that slot names are symbols, so the package matters. When you write (slot-value slot-i-am-interested 'references), you're looking for a slot named CL-USER::REFERENCES, but the slot actually seems to be named MITO.CLASS.COLUMN::REFERENCES.jkiiski
@jkiiski Thank you! Replacing 'REFERENCES with 'MITO.CLASS.COLUMN::REFERENCES solved my problem. It's been a while since I read about packages in lisp, so I can't remember many of details about handling symbols and packages. If you provide your comment as an answer I would be glad to accept it. How did you know it was MITO.CLASS.COLUMN:: and not e.g. MITO.DAO.COLUMN:: or MITO.DAO.TABLE::?Gabriel Francischini
@jkiiski I grepped both the error and the inspect output and neither mentions MITO.CLASS.COLUMN. It looks like a magical value, but it works. How you discovered/guessed it?Gabriel Francischini
feel free to open an issue or to contribute to the Cookbook !Ehvince

1 Answers

4
votes

Slot names are symbols, so the package matters when using SLOT-VALUE / WITH-SLOTS. In this case the references slots appears to be named by an internal symbol in the package MITO.CLASS.COLUMN.

The inspector doesn't show the package name (because it's rarely needed), but you can see that the slot definition is of type MITO.DAO.COLUMN:DAO-TABLE-COLUMN-CLASS, so you can use CLOSER-MOP:CLASS-SLOTS to find the slot definition:

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

Alternatively you could have tried (apropos "references") to see all packages with such symbol interned:

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