From: Rob St. Amant
Subject: Overriding defstruct functions with class methods
Date: 
Message-ID: <evdqhc$dg7$1@blackhelicopter.databasix.com>
I'm running into a problem that I wonder whether an expert can help me
with.  I'm extending a code base that relies on data structures
defined using defstruct.  I'd like to override these with CLOS
definitions, at least at the level of having method names that are the
same as the function names generated by defstruct.  The approach I've
taken so far, which works under MCL and CMUCL, involves fmakunbound on
function-name and (setf function-name).  This doesn't work in
Lispworks, and I wonder whether I'm relying on unspecified behavior in
the first two Lisp implementations or whether it's a bug in Lispworks.
Here's an example:

CL-USER 1 > (defstruct (struct) slot)
STRUCT

CL-USER 2 > (let ((object (make-struct))) (setf (struct-slot object) 10))
10

CL-USER 3 > (progn (fmakunbound '(setf struct-slot)) (fmakunbound 'struct-slot))
STRUCT-SLOT

CL-USER 4 > (let ((object (make-struct))) (setf (struct-slot object) 10))

;;; OpenMCL and CMUCL give an error here, as expected, while Lispworks
;;; returns 10.  Aborting from the error. . .

CL-USER 5 > (defclass c () ((slot :accessor struct-slot)))
#<STANDARD-CLASS C 100E2033>

CL-USER 6 > (let ((object (make-instance 'c))) (setf (struct-slot object) 10))

;;; OpenMCL and CMUCL return 10 here, while Lispworks gives an error.

Any thoughts or alternatives?  (One solution is to conditionalize the
loading of the defstruct forms, since the functionality is reproduced
in any case, but I'm curious what and where the problem is.)

From: Pascal Costanza
Subject: Re: Overriding defstruct functions with class methods
Date: 
Message-ID: <57vafuF2f0ic7U1@mid.individual.net>
Rob St. Amant wrote:
> I'm running into a problem that I wonder whether an expert can help me
> with.  I'm extending a code base that relies on data structures
> defined using defstruct.  I'd like to override these with CLOS
> definitions, at least at the level of having method names that are the
> same as the function names generated by defstruct.  The approach I've
> taken so far, which works under MCL and CMUCL, involves fmakunbound on
> function-name and (setf function-name).  This doesn't work in
> Lispworks, and I wonder whether I'm relying on unspecified behavior in
> the first two Lisp implementations or whether it's a bug in Lispworks.

IIRC, it is not specified whether setters are implemented as functions 
or as macros, so you are probably walking on undefined territory here.

My suggestion would be to use the package system to shadow the 
respective names for your own packages and then provide new definitions 
as generic functions which in their unspecialized methods simply call 
the original definitions from the original package.


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Rob St. Amant
Subject: Re: Overriding defstruct functions with class methods
Date: 
Message-ID: <evdstq$kh9$1@blackhelicopter.databasix.com>
Pascal Costanza <··@p-cos.net> writes:

> Rob St. Amant wrote:
>> I'm running into a problem that I wonder whether an expert can help me
>> with.  I'm extending a code base that relies on data structures
>> defined using defstruct.  I'd like to override these with CLOS
>> definitions, at least at the level of having method names that are the
>> same as the function names generated by defstruct.  The approach I've
>> taken so far, which works under MCL and CMUCL, involves fmakunbound on
>> function-name and (setf function-name).  This doesn't work in
>> Lispworks, and I wonder whether I'm relying on unspecified behavior in
>> the first two Lisp implementations or whether it's a bug in Lispworks.
>
> IIRC, it is not specified whether setters are implemented as functions
> or as macros, so you are probably walking on undefined territory here.
>
> My suggestion would be to use the package system to shadow the
> respective names for your own packages and then provide new
> definitions as generic functions which in their unspecialized methods
> simply call the original definitions from the original package.

Ah, thanks!  That explanation hadn't occurred to me.  And that's a
nice solution.
From: Pascal Bourguignon
Subject: Re: Overriding defstruct functions with class methods
Date: 
Message-ID: <87fy78odx8.fsf@voyager.informatimago.com>
·······@ncsu.edu (Rob St. Amant) writes:

> Pascal Costanza <··@p-cos.net> writes:
>> My suggestion would be to use the package system to shadow the
>> respective names for your own packages and then provide new
>> definitions as generic functions which in their unspecialized methods
>> simply call the original definitions from the original package.
>
> Ah, thanks!  That explanation hadn't occurred to me.  And that's a
> nice solution.

Another way would be to use the conc-name option of defstruct if it's
under your control.

(defstruct (my-struct (:conc-name %my-struct-)) a b c)
so you get %my-struct-a, %my-struct-b etc instead of my-struct-a,
and you can then write:
(defmethod my-struct-a ((self my-struct)) (%my-struct-a self))
...

Of course, you can put all this together in your own
define-structure-with-clos-accessor macro.


-- 
__Pascal Bourguignon__
http://www.informatimago.com
http://pjb.ogamita.org
From: Tim Bradshaw
Subject: Re: Overriding defstruct functions with class methods
Date: 
Message-ID: <1176195351.475137.146880@l77g2000hsb.googlegroups.com>
On Apr 10, 9:02 am, Pascal Bourguignon <····@informatimago.com> wrote:

> Another way would be to use the conc-name option of defstruct if it's
> under your control.
> [...]
> Of course, you can put all this together in your own
> define-structure-with-clos-accessor macro.

This is a lovely solution I think.
From: Rob St. Amant
Subject: Re: Overriding defstruct functions with class methods
Date: 
Message-ID: <evge6p$f9c$1@blackhelicopter.databasix.com>
Pascal Bourguignon <···@informatimago.com> writes:

> ·······@ncsu.edu (Rob St. Amant) writes:
>
>> Pascal Costanza <··@p-cos.net> writes:
>>> My suggestion would be to use the package system to shadow the
>>> respective names for your own packages and then provide new
>>> definitions as generic functions which in their unspecialized methods
>>> simply call the original definitions from the original package.
>>
>> Ah, thanks!  That explanation hadn't occurred to me.  And that's a
>> nice solution.
>
> Another way would be to use the conc-name option of defstruct if it's
> under your control.
>
> (defstruct (my-struct (:conc-name %my-struct-)) a b c)
> so you get %my-struct-a, %my-struct-b etc instead of my-struct-a,
> and you can then write:
> (defmethod my-struct-a ((self my-struct)) (%my-struct-a self))
> ...
>
> Of course, you can put all this together in your own
> define-structure-with-clos-accessor macro.

Even better.  Here's a shot at it (lightly tested):

(defun car-if-list (thing)
  (if (listp thing) (car thing) thing))

(defun conc-names (name1 name2)
  (intern (concatenate 'string
                       (string (or name1 ""))
                       (string (or name2 "")))))

(defmacro define-structure-with-clos-accessor (name-and-options &rest slot-descriptions)
  (let ((name (car-if-list name-and-options))
        (options (and (listp name-and-options) (cdr name-and-options))))
    (flet ((option-supplied-p (option)
             (assoc option options))
           (get-option (option)
             (cadr (assoc option options)))
           ((setf get-option) (value option)
             (let ((cons (assoc option options)))
               (if cons
                   (setf (cadr cons) value)
                   (push (list option value) options)))))
      (let* ((old-prefix (if (option-supplied-p :conc-name) ; Need to account for nil
                             (get-option :conc-name)
                             (conc-names name "-")))
             (new-prefix (conc-names "%" old-prefix))
             (struct-type (or (get-option :type) name)))
        (setf (get-option :conc-name) new-prefix)
        `(progn
           (defstruct (,name ,@options) ,@slot-descriptions)
           ,@(loop for slot-description in slot-descriptions
                unless (stringp slot-description)
                append
                (let* ((slot-name (car-if-list slot-description))
                       (visible-accessor (conc-names old-prefix slot-name))
                       (internal-accessor (conc-names new-prefix slot-name)))
                  `((defmethod ,visible-accessor ((STRUCT ,struct-type))
                      (,internal-accessor STRUCT))
                    (defmethod (setf ,visible-accessor) (VALUE (STRUCT ,struct-type))
                      (setf (,internal-accessor STRUCT) VALUE)))))
           ',name)))))
From: Rob St. Amant
Subject: Re: Overriding defstruct functions with class methods
Date: 
Message-ID: <evgkv6$3hq$1@blackhelicopter.databasix.com>
·······@ncsu.edu (Rob St. Amant) writes:

> Pascal Bourguignon <···@informatimago.com> writes:
>
>> ·······@ncsu.edu (Rob St. Amant) writes:
>>
>>> Pascal Costanza <··@p-cos.net> writes:
>>>> My suggestion would be to use the package system to shadow the
>>>> respective names for your own packages and then provide new
>>>> definitions as generic functions which in their unspecialized methods
>>>> simply call the original definitions from the original package.
>>>
>>> Ah, thanks!  That explanation hadn't occurred to me.  And that's a
>>> nice solution.
>>
>> Another way would be to use the conc-name option of defstruct if it's
>> under your control.
>>
>> (defstruct (my-struct (:conc-name %my-struct-)) a b c)
>> so you get %my-struct-a, %my-struct-b etc instead of my-struct-a,
>> and you can then write:
>> (defmethod my-struct-a ((self my-struct)) (%my-struct-a self))
>> ...
>>
>> Of course, you can put all this together in your own
>> define-structure-with-clos-accessor macro.
>
> Even better.  Here's a shot at it (lightly tested):

Oops.  I left out several options.  Oh, well, just a few more forms to
add.