From jmc at r-project.org  Mon Jun  6 23:28:31 2011
From: jmc at r-project.org (John Chambers)
Date: Mon, 06 Jun 2011 14:28:31 -0700
Subject: [Rd] Overloading S4 methods
In-Reply-To: <BANLkTimCFhkqPq=3XowmiZVKDtwXhM4V3g@mail.gmail.com>
References: <BANLkTi=KwUzxy+3YA+HWmP-PPozD-g7zkg@mail.gmail.com>	<4DE6631A.9010104@fhcrc.org>
	<BANLkTimCFhkqPq=3XowmiZVKDtwXhM4V3g@mail.gmail.com>
Message-ID: <4DED467F.1080006@r-project.org>

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


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



[Rd] Overloading S4 methods
John Chambers