From luke-tierney at uiowa.edu  Tue Jun  7 19:36:42 2011
From: luke-tierney at uiowa.edu (luke-tierney at uiowa.edu)
Date: Tue, 7 Jun 2011 12:36:42 -0500
Subject: [Rd] Overloading S4 methods
In-Reply-To: <4DEE5A7B.8030903@r-project.org>
References: <BANLkTi=KwUzxy+3YA+HWmP-PPozD-g7zkg@mail.gmail.com>
	<4DE6631A.9010104@fhcrc.org>
	<BANLkTimCFhkqPq=3XowmiZVKDtwXhM4V3g@mail.gmail.com>
	<4DED467F.1080006@r-project.org>
	<alpine.DEB.2.00.1106070812100.1796@luke-inspiron>
	<4DEE5A7B.8030903@r-project.org>
Message-ID: <alpine.DEB.2.00.1106071231360.1796@luke-inspiron>

On Tue, 7 Jun 2011, John Chambers wrote:

> Yes, Dylan is in many ways more authoritarian than R!  Possibly also with 
> fewer users to be annoyed.

Huh? A convention in a programming book is more authoritarian than a
warning as you are proposing (not that I am opposed to that -- I think
it's probably idea)?

Number of users of Dylan is definitely smaller, especially now, but
level of academic-level CS involvement in language design is
not. There is a lot of work in that literature that really is worth
looking at.

Best,

luke

> We might go to a warning as Iago suggests.  If we did add a warning, it would 
> be likely be more useful in the setMethod() call than at CMD check time, 
> after the package has been designed and implemented.
>
> John
>
> On 6/7/11 6:15 AM, luke-tierney at uiowa.edu wrote:
>> On Mon, 6 Jun 2011, John Chambers 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.
>> 
>> Dylan is simliar in using a generic funciton model. One of the Dylan
>> books -- I forget which one -- strongly recomends that a library only
>> define a method if either it also defines the generic or if it defines
>> one of the classes the method is specialized on. THis isn't an enforced
>> requirement but a strong recommendation.
>> 
>> Best,
>> 
>> luke
>> 
>>> 
>>> 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
>>> 
>>> ______________________________________________
>>> R-devel at r-project.org mailing list
>>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>> 
>> 
>

-- 
Luke Tierney
Statistics and Actuarial Science
Ralph E. Wareham Professor of Mathematical Sciences
University of Iowa                  Phone:             319-335-3386
Department of Statistics and        Fax:               319-335-3017
    Actuarial Science
241 Schaeffer Hall                  email:      luke at stat.uiowa.edu
Iowa City, IA 52242                 WWW:  http://www.stat.uiowa.edu



[Rd] Overloading S4 methods
luke-tierney at uiowa.edu