Search code examples
common-lisppackageclos

Access CLOS-object slots from used external package


I am learning to structure my CL programm and now having trouble to use the CLOS while programming in the large with packages.

package.lisp

(defpackage :my-project.a
  (:use :cl)
  (:export 
   create-my-object
   my-object
   ; EXPORT SINGLE SLOTS?
   my-slot-1
   ; my-slot-...
   ; my-slot-n

   ; OR EXPORT ALL ACCESSOR-FUNCTIONS?
   my-slot-1-accessor
   ; my-slot-n-accessor...
 )) 

(defpackage :my-project.b
  (:use :cl :my-project.a)
  (:export print-object-slot))

src.lisp

While the class MY-OBJECT is defined in MY-PROJECT.A

(in-package :my-project.a)

(defclass my-object ()
   ((my-slot-1 :accessor my-slot-1-accessor :initarg :my-slot-1)
     ;... more slots
     ; (my-slot-2 :accessor my-slot-2-accessor :initarg :my-slot-2)        
     ; (my-slot-n :accessor my-slot-n-accessor :initarg :my-slot-n)
     ))

as some CREATOR function for the objects

(defun create-my-object ()
  (make-instance 'my-object
                 :my-slot-1 "string" 
                 ;; further slots...
                 ))

Having some function e.g. PRINT-OBJECT in the package MY-PROJECT.B, which should handle the object instanciated from a function

(in-package :my-project.b)

(defun print-object-slot (slot-name object)
  (format nil "slot-value: ~a" (SLOT-VALUE object slot-name)))

Problem

While executing following code doesn't work

(in-package :my-project.b)

(describe 'my-object) ; works

(print-object-slot 
  'my-slot-1   ; while this works: 'my-project.a:my-slot-1   [if slot is exported]  
   (create-my-object))  

;; ==> slot MY-PROJECT.B:MY-SLOT-1 is missing from the object
;;     MY-PROJECT.A:MY-OBJECT

To access my slots programmatically, in this situation I would need to merge the originating package-name with the slot-name, to get/setf the slot from external classes...

My understanding

The accessor-functions from CLOS objects are generic functions, belonging to the package, where they have been defined via DEFCLASS, in this case: MY-PROJECT.A

By (use-package :my-project.a) in MY-PROJECT.B, the exported symbols are imported, that's why DESCRIBE works. But the symbols of the generic-slot-accessor-functions aren't included.

  1. Consideration: The architecture of the programm should NOT be planned to share/export objects and slot-access. It's not well designed to bulk-import/export slots/accessor-functions.

  2. Consideration: You can build a custom function, which get/sets the slots via the slot-accessor-function inside their package, so there is just one interface function to export?

My question:

This way handling external CLOS objects doesnt seem to be the way to go. How to export/import those accessor-functions in a sane way, without listing manually every single slot?

Edit/Solution

My terminolgy and use of slots vs. accessor-functions is a cause of this problem (thank you so much @RainerJoswig for clearing terminology up).

I did'nt use an exported version of MY-SLOT-1-ACCESSOR function, which would work as expected, but would need my to "bulk-export" them, if I would like to have access all slots in every other external package. @sds did a great job to show how to do this, and also at pointing out the general problem of my approach. Many thanks :)

In my mind, I wished to export just the object and gain full access to all the internal functions. But that's the wrong way for CLOS, since symbols and methods don't share direct bindings to the class/object, and I have to adapt better organisation of code.


Solution

  • Exporting all accessors

    You can use MOP to get the list of readers and writers for your class and then export all of them, using

    like this:

    (dolist (slot (class-direct-slots (find-class 'your-class-name)))
      (dolist (reader (slot-definition-readers slot))
        (export reader)))
    

    Why is it so complicated?

    Because you do not want to do that.

    All code which needs indiscriminate access to all slots of a class should be in the same package as the class.

    The only symbols you export should be those you need to export, and they should be explicitly vetted by you.