On 22 April 2013 01:17, Tobias Brandt <tob.bra...@gmail.com> 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 <tob.bra...@gmail.com> 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).