From jmc at r-project.org  Wed Mar 26 17:40:31 2014
From: jmc at r-project.org (John Chambers)
Date: Wed, 26 Mar 2014 09:40:31 -0700
Subject: [Rd] Conflicting definitions for function redefined as S4
	generics
In-Reply-To: <5332DFFD.6060602@bioinf.jku.at>
References: <5332B958.5020703@bioinf.jku.at>	<CAOQ5NyfQ8eFc2mLODPR-FB=71UuUjVZjz4srfmRSj-TOJBNmOQ@mail.gmail.com>	<CADwqtCOPEtXcj_06=PGnrEBMOkyag+KonAM9WKiunL2M8WYp5w@mail.gmail.com>	<5332DAB1.3090201@gmail.com>
	<5332DFFD.6060602@bioinf.jku.at>
Message-ID: <533302FF.4010301@r-project.org>

I haven't looked at this in detail, but my guess is the following is the 
distinction:

A simple call setGeneric("sort") makes a generic of the existing 
function _with the existing package_:

 > setGeneric("sort")
[1] "sort"
 > sort
standardGeneric for "sort" defined from package "base"

function (x, decreasing = FALSE, ...)
standardGeneric("sort")
<environment: 0x7fdf74335640>
Methods may be defined for arguments: x, decreasing
Use  showMethods("sort")  for currently available ones.

The same thing will, I believe, happen automatically if one calls 
setMethod() without a prior call to setGeneric().

What BioGenerics does is different:  it excludes the two trailing 
arguments and so creates a new generic in its own namespace.

Similarly (from the global environment in this case):

 > setGeneric("sort", signature="x")
Creating a new generic function for 'sort' in the global environment
[1] "sort"
 > sort
standardGeneric for "sort" defined from package ".GlobalEnv"

function (x, decreasing = FALSE, ...)
standardGeneric("sort")
<environment: 0x7fd33b21bb78>
Methods may be defined for arguments: x
Use  showMethods("sort")  for currently available ones.


When packages are loaded, the methods in the new package are installed 
in the generic function (in memory) that corresponds to the information 
in the methods as to generic name and package slot.

As Duncan points out, it's essential to keep functions of the same name 
but different packages distinct.  Like all R objects, generic functions 
are referred to by the combination of a name and an environment, here a 
package namespace.

Just how this sorts out into the symptoms reported I can't say, but I 
suspect this is the underlying issue.

John





On 3/26/14, 7:11 AM, Ulrich Bodenhofer wrote:
> First of all, thanks for the very interesting and encouraging replies
> that have been posted so far!
>
> Let me quickly add what I have tried up to now:
>
> - setMethod("sort", signature("ExClust"), function(x, decreasing=FALSE,
> %...%) %...% , sealed=TRUE) without any call to setGeneric(), i.e.
> assuming that setMethod() would implicitly create an S4 generic out of
> the S3 method sort(). Note that '%...%' in the code snippet stands for
> some details that I left out.
>
> - setGeneric("sort", def=function(x, decreasing=FALSE, ...)
> standardGeneric("sort")), i.e. consistency with the S3 generic of sort()
> in 'base', plus the call to setMethod() as shown above.
>
> - setGeneric("sort", signature="x"), i.e. consistency with the generic's
> definition in BiocGenerics, as suggested by Martin Morgan, plus the call
> to setMethod() as shown above.
>
> For all three trials, the result was exactly the same: (1) everything
> works nicely if I load BiocGenerics before apcluster; (2) if I load
> BiocGenerics after apcluster, apcluster's sort() function is broken and
> gives the following error:
>
> Error in rank(x, ties.method = "min", na.last = "keep") :
>    unimplemented type 'list' in 'greater'
> In addition: Warning message:
> In is.na(x) : is.na() applied to non-(list or vector) of type 'S4'
>
> Obviously, sort() is dispatched to the definition made by BiocGenerics:
>
>  > showMethods("sort", includeDefs=TRUE)
> Function: sort (package BiocGenerics)
> x="ANY"
> function (x, decreasing = FALSE, ...)
> {
>      if (!is.logical(decreasing) || length(decreasing) != 1L)
>          stop("'decreasing' must be a length-1 logical vector.\nDid you
> intend to set 'partial'?")
>      UseMethod("sort")
> }
>
> So the method registered for class 'ExClust' is  lost if BiocGenerics is
> attached. Just for your information: all these tests have been done with
> R 3.0.2 and Bioconductor 2.13 (BiocGenerics version 0.8.0).
>
> Thanks and best regards,
> Ulrich
>
>
>
> On 03/26/2014 02:48 PM, Duncan Murdoch wrote:
>> On 26/03/2014, 9:13 AM, Gabriel Becker wrote:
>>> Perhaps a patch to R such that generics don't clobber each-other's
>>> method
>>> tables if the signatures agree? I haven't dug deeply, but simply merging
>>> the method tables seems like it would be safe when there are no
>>> conflicts.
>>>
>>> That way this type of multiplicity would not be a problem, though it
>>> wouldn't help (as it shouldn't) if the two generics didn't agree on
>>> signature or both carried methods for the same class signature.
>>
>> I don't think R should base the decision on the signature.
>>
>> There are two very different situations where this might come up. In
>> one, package A and package B might both define a generic named foo()
>> that happens to have the same signature, but with nothing in common.
>> That should be allowed, and should behave the same as when they both
>> create functions with the same name:  it should be up to the user to
>> specify which generic is being called.  If R merged the two generics
>> into one, there would be chaos.
>>
>> The other situation is more likely to apply to this case.  It sounds
>> as though both apcluster and BiocGenerics are creating a sort()
>> generic by promoting the base package S3 generic into an S4 generic.
>> Clearly they should not be creating separate generics, there's just one.
>>
>> I don't know if there's something wrong with the way apcluster or
>> BiocGenerics are doing things, or something wrong with the way the
>> methods package is creating the generic, but it sure looks like a bug
>> somewhere.
>>
>> Duncan Murdoch
>>
>>>
>>> ~G
>>>
>>>
>>> On Wed, Mar 26, 2014 at 4:38 AM, Michael Lawrence
>>> <lawrence.michael at gene.com
>>>> wrote:
>>>
>>>> The BiocGenerics package was designed to solve this issue within
>>>> Bioconductor. It wouldn't be the worst thing in the world to depend
>>>> on the
>>>> simple BiocGenerics package for now, but ideally the base generics
>>>> would be
>>>> defined higher up, perhaps in the methods package itself. Maybe someone
>>>> else has a more creative solution, but any sort of conditional/dynamic
>>>> approach would probably be too problematic in comparison.
>>>>
>>>> Michael
>>>>
>>>>
>>>>
>>>> On Wed, Mar 26, 2014 at 4:26 AM, Ulrich Bodenhofer <
>>>> bodenhofer at bioinf.jku.at
>>>>> wrote:
>>>>
>>>>> [cross-posted to R-devel and bioc-devel]
>>>>>
>>>>> Hi,
>>>>>
>>>>> I am trying to implement a 'sort' method in one of the CRAN
>>>>> packages I am
>>>>> maintaining ('apcluster'). I started with using setMethod("sort",
>>>>> ...) in
>>>>> my package, which worked fine. Since many users of my package are from
>>>> the
>>>>> bioinformatics field, I want to ensure that my package works smoothly
>>>> with
>>>>> Bioconductor. The problem is that the BiocGenerics package also
>>>>> redefines
>>>>> 'sort' as an S4 generic. If I load BiocGenerics before my package,
>>>>> everything is fine. If I load BiocGeneric after I have loaded my
>>>>> package,
>>>>> my setMethod("sort", ...) is overridden by BiocGenerics and does
>>>>> not work
>>>>> anymore. A simple solution would be to import BiocGenerics in my
>>>>> package,
>>>>> but I do not want this, since many of this package's users are outside
>>>> the
>>>>> bioinformatics domain. Moreover, I am reluctant to include a
>>>>> dependency
>>>> to
>>>>> a Bioconductor package in a CRAN package. I thought that maybe I could
>>>>> protect my setMethod("sort", ...) from being overridden by
>>>>> BiocGeneric by
>>>>> sealed=TRUE, but that did not work either. Any ideas are gratefully
>>>>> appreciated!
>>>>>
>>>>> Thanks a lot,
>>>>> Ulrich
>
> ______________________________________________
> R-devel at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel



[Rd] Conflicting definitions for function redefined as S4 generics
John Chambers