make-instance in defconstant

Discussion of Common Lisp
Post Reply
dangerousdan
Posts: 2
Joined: Mon Nov 23, 2009 2:13 am

make-instance in defconstant

Post by dangerousdan » Mon Nov 23, 2009 3:06 am

Hello,

I need to set a constant to be one instance of a class, but when I use:

Code: Select all

(defconstant +html-view+ (make-instance 'html-view))
the compile fails with "no such class 'html-view" (compiled in slime via load-system). I can fix this by slapping an eval-when around the defclass call, but this strikes me as pretty bizarre. Wouldn't defclass already be defined at load/compile time anyway? It also causes problems in SBCL as I then start getting the "constant redefined" error (presumably asdf is reloading the files several times for some reason, perhaps something to do with the dependancies).

Code:

Code: Select all

(defclass html-view ()
   ())
(defconstant +html-view+ (make-instance 'html-view))

(defgeneric render (component view))

(defmethod render ((component standard-object) (view html-view))
  ;...
   )

(defun test-it ()
       (render (make-instance 'web-page) +html-view+))
I suppose since what I'm doing here is using html (and xml) singletons I could just specialize on the meta-class instead but I don't like this for later when I start adding methods and slots of these views.

Thanks in advance,
Jason

ramarren
Posts: 613
Joined: Sun Jun 29, 2008 4:02 am
Location: Warsaw, Poland
Contact:

Re: make-instance in defconstant

Post by ramarren » Tue Nov 24, 2009 11:45 pm

dangerousdan wrote:Wouldn't defclass already be defined at load/compile time anyway?
Not necessarily. The class name is associated with a class, but the class itself is not yet created enough to be instantiated at compile time. My suggestion would be to put class definitions in a different file than DEFCONSTANT.
dangerousdan wrote:It also causes problems in SBCL as I then start getting the "constant redefined" error (presumably asdf is reloading the files several times for some reason, perhaps something to do with the dependancies).
Use DEFINE-CONSTANT from Alexandria with a proper test to resolve this.

But, in general, using DEFCONSTANT with anything else than characters, symbols or numbers is pointless. Just use DEFPARAMETER, there will be no difference, since object instances can't be inlined anyway, and they are not really constant anyway (DEFCONSTANT doesn't give any guarantees that the internal state of the object would not change).

Also, why is +html-view+ in the example given even a class? You can specialize methods on symbols with EQL specializers, like this:

Code: Select all

(defgeneric render (component view))

(defmethod render ((component standard-object) (view (eql 'html-view)))
  ;;...
  )

(defun test-it ()
  (render (make-instance 'web-page) 'html-view))

dangerousdan
Posts: 2
Joined: Mon Nov 23, 2009 2:13 am

Re: make-instance in defconstant

Post by dangerousdan » Wed Nov 25, 2009 12:58 am

Thanks for the quick reply.
Ramarren wrote:My suggestion would be to put class definitions in a different file than DEFCONSTANT.
I thought about doing this, but I only considered having a single constants file, which would have a circular dependency problem (i.e. normally the constants file would be the file all others depend on, but in this case it depends on other files). I suppose I could make a special "view-constants" file. I think I'll try that. Good suggestion.
Ramarren wrote:Use DEFINE-CONSTANT from Alexandria with a proper test to resolve this.
I looked into using it, but I saw several strong arguments against doing so (e.g. it makes defconstant equivalent to defparameter, it doesn't work in certain cases anyway, etc.).
Ramarren wrote: But, in general, using DEFCONSTANT with anything else than characters, symbols or numbers is pointless. Just use DEFPARAMETER, there will be no difference, since object instances can't be inlined anyway, and they are not really constant anyway (DEFCONSTANT doesn't give any guarantees that the internal state of the object would not change).
Fair argument. I guess I wanted to use constant so they can be + variables instead of * variables (i.e. seeing +html-view+ defined with defparameter instead of defconstant might seem odd, no?) since the code would never change them.
Ramarren wrote: Also, why is +html-view+ in the example given even a class? You can specialize methods on symbols with EQL specializers, like this:

Code: Select all

(defgeneric render (component view))

(defmethod render ((component standard-object) (view (eql 'html-view)))
  ;;...
  )

(defun test-it ()
  (render (make-instance 'web-page) 'html-view))
html-view is a class and can have it's own slots and specializers. At this early stage nothing has been added to it, but there will be.

ramarren
Posts: 613
Joined: Sun Jun 29, 2008 4:02 am
Location: Warsaw, Poland
Contact:

Re: make-instance in defconstant

Post by ramarren » Wed Nov 25, 2009 1:20 am

I thought about a different way to do it, which although not all that sane is, I think, over-engineered in a funny way:

Code: Select all

(defparameter *view-cache* (make-hash-table))

(defconstant +html-view+ 'html-view)

(defgeneric get-or-create-view (view-designator)
  (:method ((view-designator symbol))
    (or (gethash view-designator *view-cache*)
        (setf (gethash view-designator *view-cache*)
              (make-instance view-designator)))))

(defmethod render ((component t) (view symbol))
  (render component
          (get-or-create-view view)))
And the GET-OR-CREATE-VIEW function can even be specialized on either concrete symbols, or non-symbols, to give different behaviour.

Post Reply