guile-user
[Top][All Lists]
Advanced

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

Re: GOOPS: calling next-method with different arguments.


From: Tobias Brandt
Subject: Re: GOOPS: calling next-method with different arguments.
Date: Mon, 22 Apr 2013 08:42:57 +0200

> When changing the _type_ of an argument this will perhaps not have the
> desired result.  You have to consider the specific situation quite
> careful.

I'm aware of that. The CL hyperspec comes with the same warning. 
I would probably only use it for initialize, where the next method
is basically the super class constructor.

I have to wonder, what is the purpose of your real <bar> anyway?  Is
> it just a convenience so you can construct the base class using a
> number instead of a string?

There is no real <bar>, it was just a stupid example. I was hoping to do some
OOP in Scheme in the future and thought about how things translate from Java/C#.
I know, it's not always a could idea to just literally translate things from one
language to another.

Cheers,
Tobias


On 22 April 2013 06:39, Daniel Hartwig <address@hidden> wrote:
On 22 April 2013 01:17, Tobias Brandt <address@hidden> wrote:
> I just noticed something: next-method *already* supports calling it with
> different arguments. It's just not documented.
>

When changing the _type_ of an argument this will perhaps not have the
desired result.  You have to consider the specific situation quite
careful.

The list of applicable methods is already computed before
‘next-method’ is invoked, and is based on the argument types in the
original call.  If you change any of these types the applicable
methods are not recomputed, and neither should they be.  The entire
applicable list and call tree will potentially be quite different.  So
this will only work when the _already computed_ next method is
compatible with the new argument types, but even then, the method you
arrive at may be less specialized than is desired (i.e. dispatching on
<object> rather than <string>).

In your actual use case involving object construction, you can
specialize ‘initialize’ (see later) and ‘next-method’ will work fine
because ‘initialize’ is not dispatching on argument types other than
the class.  The next method is the next superclass' intializer in most
situations, which is what you want.

Generally however, and in cases like the example you gave for ‘f’, it
will not work to change argument types with ‘next-method’.  Suppose
you want a method specialized on the base class and a string:

(define-method (g (foo <foo>) (x <string>))
  (format #t "foo: ~a\n" x))

and another method specialized on the derived class and a number:

(define-method (g (bar <bar>) (x <number>))
  (next-method self (number->string x))
  (format #t "bar: ~a\n" x))

When you make the original call, the list of applicable methods is:

(compute-applicable-methods g (list (make <bar>) 1))
=> (#<<method> (<bar> <number>) 99840a0>)

Note that the method dispatching on <foo> is not in that list, indeed
there is only a single applicable method.  Hence, the call to
‘next-method’ within the second method will throw an error:

(g (make <bar>) 1)
ERROR: In procedure scm-error:
ERROR: No next method when calling #<<generic> g (2)>
with arguments (#<<bar> 95d2138> "1")

In a situation like this, just call the desired generic directly:

(define-method (g (bar <bar>) (x <number>))
  (g bar (number->string x))
  (format #t "bar: ~a\n" x))

which will be dispatched correctly considering the new type.  This is
effectively what you have done with ‘make’ in your previous post.  The
key point to take away is that when the argument type has been
significant in dispatching the call, it is probably not correct to use
‘next-method’.

> On 21 April 2013 18:05, Tobias Brandt <address@hidden> wrote:
>>
>> Hi,
>>
>> thanks for your input. I tried to avoid the whole next-method issue
>> entirely and defined a method for make on bar's metaclass instead.
>>
>> (use-modules (oop goops))
>>
>> (define-class <foo> () (s #:init-keyword #:s))
>> (define-class <bar-class> (<class>))
>> (define-class <bar> (<foo>) #:metaclass <bar-class>)
>>
>> (define-method (make (self <bar-class>) (i <integer>))
>>     (make self #:s (number->string i)))
>>

I would actually call that argument ‘class’ rather than ‘self’.

Specializing ‘make’ like this is perhaps inconvenient for classes
derived from <bar>, or when there are more slots to initialize with
keywords.  It does not take any rest argument, and the integer
argument omits a keyword that is otherwise typical of ‘make’ and
‘initialize’ methods.  Instead, you can specialize ‘initialize’ and
avoid using a custom metaclass and atypical ‘make’:

;; Using a unique keyword #:i for <bar>:
(use-modules (ice-9 optargs))
(define-method (initialize (bar <bar>) initargs)
  (let-keywords initargs #t
      (i)
    (cond (i (next-method bar (append `(#:s ,(number->string i))
                                      initargs)))
          (else (next-method)))))


I have to wonder, what is the purpose of your real <bar> anyway?  Is
it just a convenience so you can construct the base class using a
number instead of a string?  If so, why not just use:

(define-method (make-bar (i <integer>))
  (make <foo> #:s (number->string i)))

>> However, I have no idea what the performance implications of creating a
>> new metaclass are. IIRC, in Smalltalk every class has its own metaclass
>> automatically and that doesn't seem to cause any problems.
>>

Likewise in GOOPS, all objects use a metaclass (usually <class> in
typical cases).


reply via email to

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