[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/