From jmc at r-project.org  Wed Jun  8 18:57:46 2011
From: jmc at r-project.org (John Chambers)
Date: Wed, 08 Jun 2011 09:57:46 -0700
Subject: [Rd] Overloading S4 methods
In-Reply-To: <BANLkTi=RRJCCBvLjfVO62A=uG8R9wm-6Nw@mail.gmail.com>
References: <BANLkTi=KwUzxy+3YA+HWmP-PPozD-g7zkg@mail.gmail.com>	<4DE6631A.9010104@fhcrc.org>	<BANLkTimCFhkqPq=3XowmiZVKDtwXhM4V3g@mail.gmail.com>	<4DED467F.1080006@r-project.org>
	<BANLkTi=RRJCCBvLjfVO62A=uG8R9wm-6Nw@mail.gmail.com>
Message-ID: <4DEFAA0A.8000405@r-project.org>

The bug should be fixed in r-devel and 2.13 patched, as of svn rev. 56090.

John

On 6/7/11 12:42 AM, Iago Mosqueira wrote:
> On Mon, Jun 6, 2011 at 11:28 PM, John Chambers<jmc at r-project.org>  wrote:
>> This is a bug, medium-subtle, but also raises an interesting software design
>> point.
>>
>> The Bug:
>>
>> Nothing specific about "ANY" and "missing", but the issue is whether the
>> method was inherited (the "ANY" case) or defined directly (the "missing"
>> case).
>>
>> Generic functions keep a cached table of dispatched methods, to save
>> determining inherited methods repeatedly for calls with the same signature.
>>   When pkg B is loaded, the inherited methods are reset, but apparently the
>> directly defined ones were not (but should have been if pkg B overrides the
>> method).
>>
>> It's interesting that this bug seems not to have been reported before, which
>> leads to:
>>
>> The Software Design Point:
>>
>> When a package (B) extends the class/method software in another package (A),
>> typically B adds new classes and perhaps new generic functions with methods
>> for previous classes in A as well as classes in B.  It might also extend the
>> behavior for classes in A to other generic functions.
>>
>> What is less usual is to directly override an existing method for a class
>> that belongs to A.  Notice that there can be side-effects, such as behavior
>> of examples or tests in package A depending on whether B has been loaded or
>> not.  And objects created entirely from A could have their computations
>> change after B was loaded.
>>
>> Nothing at all illegal here, and we'll make it work.  But a more predictable
>> implementation for most applications would, say, define a new class in B
>> that extended the class in A.  In your example (very helpful, by the way)
>> one might have a class "mynumB", perhaps with the same slots as "mynum" but
>> with modified behavior.
>>
>> If you want to keep the current implementation, though, a workaround until
>> the bug is fixed would be something like:
>>
>> setMethod("plot", c("mynum", "missing"), getMethod("plot", c("mynum",
>> "missing")))
>>
>> executed after B is attached (I think it could be in the .onLoad function
>> for B, but have not tested that).
>>
>> John
>
> Many thanks for the very complete explanation. We are using this
> mechanism to provide ggplot2-based plot for some classes, to
> substitute the initial lattice-based ones, so the effects are limited
> to visual output and not results of computation, but it is good you
> reminded me of the possible side effects of this strategy. Is maybe a
> warning during R CMD check appropriate here?
>
>
> Adding the call to setMethod(..., getMethod()) did not work if placed
> inside .onLoad, and in fact had the effect of getMethod() now
> returning the Apkg method after Bpkg was loaded.
>
> Running the line after Bpkg has loaded did sort it out. What would
> then be the best way of adding this command to the pkg loading
> process? I have also tried adding it to a zzz.R file on iuts own but
> that did not work, I still need to re-run it after loading has
> finished.
>
> Many thanks,
>
>
> Iago
>
>>
>>
>> On 6/6/11 4:11 AM, Iago Mosqueira wrote:
>>>
>>> On Wed, Jun 1, 2011 at 6:04 PM, Martin Morgan<mtmorgan at fhcrc.org>    wrote:
>>>>
>>>> On 06/01/2011 04:39 AM, Iago Mosqueira wrote:
>>>>>
>>>>> Dear all,
>>>>>
>>>>> I am experiencing some problems with S4 method overloading. I have
>>>>> defined a generic for graphics:plot, using
>>>>>
>>>>> setGeneric("plot", useAsDefault = plot)
>>>>>
>>>>> and with
>>>>>
>>>>> importFrom('graphics', 'plot') and
>>>>>
>>>>> exportMethods('plot') in the NAMESPACE file of pkg A.
>>>>
>>>> I'd guess you were creating two generics (explicitly in pkgA, implicitly
>>>> in
>>>> pkgB). Maybe
>>>>
>>>>   export(plot)
>>>>
>>>> in NAMESPACE of pkg A,
>>>>
>>>>   importFrom('pkgA', plot)
>>>>   exportMethods(plot)
>>>>
>>>> in pkg B. Feel free to post to the list if that's helpful.
>>>>
>>>> Martin
>>>>
>>>>>
>>>>> I then proceed to define a method for signature c('myS4class',
>>>>> 'missing'). This works as expected: selectMethod('plot',
>>>>> c('myS4class', 'missing')) returns the newly defined method, and the
>>>>> method gets called when invoked.
>>>>>
>>>>> Another pkg, B, wishes to overload this and redefines the method for
>>>>> the same signature. A method is defined for c('myS4class', 'missing'),
>>>>> and exported on the NAMESPACE. The new method is shown by
>>>>> selectMethod() after pkg B has been loaded, but a call to
>>>>>
>>>>> plot(anobjectofmyS4class)
>>>>>
>>>>> comes up with the result of running the first method, from pkg A. I
>>>>> have tried importing 'plot' in B's NAMESPACE from both graphics or A,
>>>>> but the end result is the same.
>>>>>
>>>>> Package B does the same thing for a method created by pkg A, myMethod,
>>>>> and that works fine.
>>>>>
>>>>> Any pointers or where this might be going wrong? How is it that a
>>>>> different method than the one shown by selectMethod() is being run?
>>>>> Something to do with the 'missing' part of the signature?
>>>>>
>>>>> Many thanks,
>>>>>
>>>>>
>>>>>
>>>>> Iago Mosqueira
>>>
>>> Dear all,
>>>
>>> I have tried Martin's suggestion, but the problem persists. It seems
>>> to be related to having 'missing' in the signature, as doing the same
>>> kind of overloading for c('myclass', 'ANY') work as expected.
>>>
>>> I am attaching 2 simple packages where I attempt this repeated
>>> overloading of plot for the same class. Script below, also found in
>>> Bpkg/tests.test.R, shows what I have encountered so far:
>>> plot('myclass', 'ANY') can be re-overloaded, but plot('myclass',
>>> 'missing') cannot in the same way. If I run
>>>
>>> trace("plot", browser, exit=browser, signature = c("mynum", "missing"))
>>>
>>> the  new method is actually called.
>>>
>>> Any hint on what I am doing wrong or where to look for an explanation
>>> will be much appreciated.
>>>
>>> Best regards,
>>>
>>>
>>> Iago Mosqueira
>>>
>>>
>>>
>>> ______________________________________________
>>> R-devel at r-project.org mailing list
>>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>
>
> ______________________________________________
> R-devel at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel



[Rd] Overloading S4 methods
John Chambers