guile-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: goops - guile-clutter unexpected bug while using #:virtual slot allo


From: Andy Wingo
Subject: Re: goops - guile-clutter unexpected bug while using #:virtual slot allocation for a <clutter-actor> subclass
Date: Fri, 06 Feb 2015 14:33:35 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.4 (gnu/linux)

Hello,

May I first please reduce your test case.  Here is an equivalent
version:

  (use-modules (oop goops))
  (define-class <a> ()
    (foo #:getter foo #:init-keyword #:foo))
  (define-class <b> (<a>))
  (define obj (make <b> #:foo 34))
  (define-method (foo (self <b>))
    (pk "ahoy!")
    (next-method))
  (pk (foo obj))
  
It has nothing to do with modules or setters, and everything to do with
GOOPS and accessors.  Anyway, this has been a confusing thread, so let
me summarize:

It used to be, in Guile 1.8, that the test case didn't work at all:
n
   guile>   (pk (foo obj))

   ;;; ("ahoy!")

   Backtrace:
   In current input:
     18: 0* [peek ...
     18: 1*  [foo #<<b> 7fc8898e0e20>]
      ?: 2   (let* ((next-method (goops:make-next-method self))) (pk "ahoy!") 
...)

   <unnamed port>: In expression (let* (#) (pk "ahoy!") ...):
   <unnamed port>: No next method when calling #<<generic> foo (2)>
   with arguments (#<<b> 7fc8898e0e20>)

It is important to note what the behavior was in Guile 1.8 because it is
the version that is closest to what Mikael originally wrote (or
inherited from stklos); though there are many ways in which GOOPS could
be better, I have been surprised again and again about the thoroughness
of the original implementation.

In Guile 2.0, I made an error that accidentally made this test case
"work", in the sense that it would return the value of the slot "foo".

   scheme@(guile-user)>   (pk (foo obj))

   ;;; ("ahoy!")

   ;;; (34)
   $1 = 34

However it wouldn't always return the value of the slot "foo"; for
example:

   scheme@(guile-user)> (define-class <c> () (bar #:init-keyword #:bar))
   scheme@(guile-user)> (define-class <ac> (<a> <c>))
   scheme@(guile-user)> (define obj2 (make <ac> #:foo 34 #:bar 42))
   scheme@(guile-user)>   (define-method (foo (self <ac>))
       (pk "wat!!!")
       (next-method))
   scheme@(guile-user)>   (pk (foo obj2))

   ;;; ("wat!!!")

   ;;; (42)
   $2 = 42

Here you see that it returns the value of the "bar" slot.  Why?  Because
defining the (foo (self <ac>)) method *replaced* the accessor method for
`foo' on instances of type <ac> with our new method, and then the
next-method that applies is the accessor method that is specialized on
values of type <a>, which looks for the slot in field 0.

I thought at first that this wasn't right, that really accessor methods
need to specialize on the types that they actually see, not just their
specializers; but that's not right, because not all subtypes of a type
have all slots of all of their supertypes.  That got me thinking and
looking back, so I found the bug that I introduced.  It turns out the
GOOPS design is to add accessor methods for each subclass that has a
given slot, and they are not designed to "inherit" -- i.e. an accessor
method specialized on <base> shouldn't be in the next-method list for
<derived>.

I have recently made a change to restore us to Guile 1.8 behavior, so as
to fix the "wat" bug above in Guile 2.0 and 2.2.

> This said and with that in mind, the implementation you say was in guile-1.8 
> and
> that it appears you have reimplemented 'right now' breaks the specification 
> and
> afaiac makes goops totally unusable for any serious work: this is a 
> catastrophy,
> please reconsider!

David, I would really appreciate it if you could tone down the
hyperbole.  Thanks in advance :)

> When talking about goops, we should not refer to 1.8 (*), 1.6 or any guile 
> version
> for that matter, but to the clos language spec subset goops implements 
> [appart from
> the adaptation for it to be module protected, but that is not the point here].

   (defclass <a> ()
     ((foo :reader foo :initarg :foo)))
   (defclass <b> (<a>) ())
   (defvar obj (make-instance '<b> :foo 34))
   (defmethod foo ((self <b>))
     (write "ahoy!")
     (call-next-method))
   (foo obj)

Interestingly, in CLISP at least this works in the way you expect: it
prints "ahoy" and then chains up and returns the value.  I also note
that it doesn't install accessor methods on each subtype; before
defining the "ahoy" method, I got this:

   > (generic-function-methods #'foo)
   (#<STANDARD-READER-METHOD (#<STANDARD-CLASS <A>>)>)

And that method worked on instances of <b> just as well.

I am not sure how to interpret this result.  The slot definition
protocol in CLOS is different; for example,
compute-effective-slot-definition in CLOS logically *combines* slot
definitions with the same name.  Could it be that in CLOS, all instances
of a type are considered to have all slots of all supertypes?  That is
not the GOOPS design, but perhaps that should change.

Andy
-- 
http://wingolog.org/



reply via email to

[Prev in Thread] Current Thread [Next in Thread]