From mtmorgan at fhcrc.org  Fri Dec 16 23:57:25 2011
From: mtmorgan at fhcrc.org (Martin Morgan)
Date: Fri, 16 Dec 2011 14:57:25 -0800
Subject: [Rd] S4 NAMESPACE method imports and exports do not include
 (promoted?) generics
In-Reply-To: <4EEBA7E4.70802@r-project.org>
References: <4EEA841C.5060709@fhcrc.org> <4EEB52D7.9010207@fhcrc.org>
	<4EEBA7E4.70802@r-project.org>
Message-ID: <4EEBCCD5.6010405@fhcrc.org>

On 12/16/2011 12:19 PM, John Chambers wrote:
> The subject heading is correct if referring to exportMethods() and
> importMethodsFrom(). They refer to the methods tables, not the generic
> functions, whatever the extensions manual says.
>
> Looking into the code of namespaceImportMethods() will illustrate this.
> It just deals with lists of method tables obtained from .getGenerics()
> which in spite of its name also only looks for the method table metadata
> objects.
>
> As I vaguely recall, there was some concern at one time about having
> extra copies of the generic version of the function.
>
> The fundamental problem is that creating methods for unique(), say, does
> not change the way calls to base::unique() work. Therefore, all packages
> that want to use methods have to ensure that a generic version of unique
> gets in between. Primitive functions are an exception because the method
> dispatch is in the C code and has a rule for checking when given an S4
> object. There is no corresponding provision for evaluating a call to a
> regular function.
>
> If the importing package has a setGeneric() for the relevant function
> then its own namespace has the generic version of the function. (That is
> a workaround, but I inferred that was what you were trying to avoid.)
>
> Fixes seem possible, but some care is needed. If exportMethods
> automatically exported the generic function, it really is no different
> from export() for that function.

export() somehow implies ownership of the generic, e.g., responsibility 
for documentation. I can see in the scenario below that PkgB might be 
expected to Depends: PkgA if it intends for the user to access PkgB's 
methods on PkgA::unique.

> namespaceImportMethods() could try to supply the generic function if it
> is not already present. If it does not find the generic in the namespace
> being imported, it would essentially have to call setGeneric(), assuming
> the non-generic function exists in the specified package (e.g., in base
> for unique()).

In the example below for PkgC the 'unique' generic is in PkgB's 
namespace imports

 > getNamespaceImports("PkgB")
$base
[1] TRUE

$PkgA
   unique
"unique"

I guess PkgB could have Depends: PkgA, not importFrom(PkgA, unique), and 
then defined and exported a method on PkgA::unique found on the search 
path, so that the generic wasn't available to PkgC. But I'd be happy if 
the generic found in either PkgB's namespace or namespace imports were 
imported along with the method. Not sure that I like the idea of calling 
setGeneric() -- PkgA could have done something non-standard -- and would 
rather an error.

Thans for your attention.

Martin

>
> Comments?
> John
>
>
>
> On 12/16/11 6:16 AM, Martin Morgan wrote:
>> tar of Pkgs A, B, C attached. Martin
>>
>> On 12/15/2011 03:34 PM, Martin Morgan wrote:
>>> In
>>>
>>> > R.version.string
>>> [1] "R Under development (unstable) (2011-12-15 r57901)"
>>>
>>> section 1.6.6 of 'Writing R Extensions' says
>>>
>>> Note that exporting methods on a generic in the namespace will
>>> also export the generic, and exporting a generic in the
>>> namespace will also export its methods.
>>>
>>> and
>>>
>>> Note that importMethodsFrom will also import any generics defined in
>>> the namespace on those methods
>>>
>>> However, if PkgA promotes 'unique' to a generic and exports that
>>>
>>> DESCRIPTION:
>>> Imports: methods
>>>
>>> R/f.R:
>>> setGeneric("unique")
>>>
>>> NAMESPACE:
>>> export(unique)
>>>
>>> and PkgB creates and exports a method on unique
>>>
>>> DESCRIPTION
>>> Imports: methods, PkgA
>>>
>>> R/f.R:
>>> setClass("B", representation(b="numeric"))
>>> setMethod(unique, "B",
>>> function(x, incomparables=FALSE, ...) unique(x at b))
>>>
>>> NAMESPACE:
>>> importFrom(PkgA, unique)
>>> exportClasses(B)
>>> exportMethods(unique)
>>>
>>> and PkgC wants to import PkgB's classes and methods
>>>
>>> DESCRIPTION
>>> Imports: methods, PkgB
>>>
>>> R/f.R
>>> cunique <- function(x) unique(x)
>>>
>>> NAMESPACE
>>> importMethodsFrom(PkgB, unique)
>>> export(cunique)
>>>
>>> then
>>>
>>> (a) the 'unique' generic is not available to the user of PkgB
>>>
>>> > library(PkgB)
>>> > unique(new("B", b=1:5))
>>> Error in unique.default(new("B", b = 1:5)) :
>>> unique() applies only to vectors
>>>
>>> and (b) the generic has not been imported to PkgC's namespace
>>>
>>> > cunique(new("B", b=1:5))
>>> Error in unique.default(b) : unique() applies only to vectors
>>>
>>> A workaround is for PkgB to also export(unique), and for PkgC to also
>>> importFrom(PkgA, unique), but is this the intention?
>>>
>>> This is arising from Bioconductor efforts to place commonly promoted
>>> functions and S3 classes into a single package, to avoid conflicts when
>>> the same function is promoted independently by several packages.
>>>
>>> Martin
>>
>>


-- 
Computational Biology
Fred Hutchinson Cancer Research Center
1100 Fairview Ave. N. PO Box 19024 Seattle, WA 98109

Location: M1-B861
Telephone: 206 667-2793



[Rd] S4 NAMESPACE method imports and exports do not include (promoted?) generics
Martin Morgan