Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 880
LeinadLime Avatar asked Jan 10 '17 17:01

LeinadLime


1 Answers

Terminology

The question does not make the differences between a slot, slot-name and a slot accessor function clear. Conflating slot names and accessor functions is not that a good idea. You should be clear what is what.

(defpackage "GUI"
  (:use "CL")
  (:export
     ;; class
     window
     window-screen
     window-width
     window-height))

(defclass window ()
  ((screen :accessor window-screen :initarg :screen)
   (width  :accessor window-width  :initarg :width  :initform 640)
   (height :accessor window-height :initarg :height :initform 400)))

Now screen is a slot name and window-screen is an accessor function.

The slot name is just a symbol. You can use any symbol for that. For example you can also write (just a random example, don't use):

(defpackage "SLOTS" (:use))
(defpackage "AC"    (:use)
  (:export
     "WINDOW-SCREEN"
     "WINDOW-WIDTH"
     "WINDOW-HEIGHT"))

(defclass window ()
  ((slots::screen :accessor ac:window-screen :initarg :screen)
   (slots::width  :accessor ac:window-width  :initarg :width  :initform 640)
   (slots::height :accessor ac:window-height :initarg :height :initform 400)))

Above would use slot names in a package slots and accessors in a package ac.

An accessor is a generic function.

So, when you write:

(defun foo (instance slot-name)
  ...)

I would expect that slot-name is a symbol, not an accessor function.

(defun foo (instance accessor)
  ...)

For above I would expect accessor to be a function, not a symbol.

If you really want to make the difference clear, you can write methods:

(defmethod foo (instance (path symbol))
  (slot-value instance path))

(defmethod foo (instance (path function))
   (funcall function instance))

What to export?

Usually I would export accessor names in a package, but not slot names.

Import?

But often I would not even import the package:

(defpackage "GUI-GAME"
  (:use "CL"))

Above package does not import package gui. It could, but here it doesn't.

(defmethod describe-window ((w gui:window))
  (format t "~% Window width:~a height:~a"
          (gui:window-width w)
          (gui:window-width h)))

The advantage is that I see two things in the source code:

  • gui:window is exported and thus part of a package interface
  • gui:window is actually from the package gui and there is no name conflict with other symbols.

Just use the symbols for the class and the accessor functions with their package names prepended.

like image 177
Rainer Joswig Avatar answered Dec 02 '22 16:12

Rainer Joswig