On Fri, Aug 30, 2013 at 11:51 AM, Henrik Bengtsson <h...@biostat.ucsf.edu> 
wrote:
> Hi.
>
> On Fri, Aug 30, 2013 at 6:58 AM, Paul Gilbert <pgilbert...@gmail.com> wrote:
>> This is related to the recent thread on correct NAMESPACE approach when 
>> writing S3 methods. If your methods are S4 I think pkgB does not need to 
>> export the generic. Just export the method and everything works magically 
>> and your problem disappears. For S3 methods there seems to be the 
>> difficultly you describe. Of course, the difference between S3 and S4 on 
>> this appears somewhat bug like. (I have not tested all this very carefully 
>> so I may have something wrong.)
>
> For the record, you're referring to R-devel thread 'Correct NAMESPACE
> approach when writing an S3 method for a generic in another package'
> started on Aug 24, 2013
> [https://stat.ethz.ch/pipermail/r-devel/2013-August/067221.html].
> Yes, it's closely related or possibly the same issue.  However, I do
> not find it odd that the S3 generic function needs to be exported
> from/available via the namespace.  Hence it needs to be export():ed by
> at least one package/namespace.
>
> The real issue is when two package needs to export a generic function
> with the same name, e.g. foo().   If the two generic functions are
> defined differently (e.g. different arguments/signatures), they will
> be in conflict with each other.  If they are identical, this should
> not be a problem, but here I might be wrong.  However, there is also
> the special case where one package reexports the generic function from
> another package, e.g. PkgB::foo() exports PkgA:foo().  In this case,
> the object 'foo' does actually not existing in the name space of
> package PkgB - instead it provides a "redirect" to it, e.g.
>
>> PkgA::foo
> function (...)
> UseMethod("foo")
> <environment: namespace:PkgA>
>
>> PkgB::foo
> function (...)
> UseMethod("foo")
> <environment: namespace:PkgA>
>
>> exists("foo", envir=getNamespace("PkgB"), inherits=FALSE)
> [1] FALSE
>
>> exists("foo", envir=getNamespace("PkgB"), inherits=TRUE)
> [1] TRUE
>
>> identical(PkgB::foo, PkgA::foo)
> [1] TRUE
>
>
> The warning on "replacing previous import by 'PkgA::foo' when loading
> 'PkgC'" that occurs due to import(PkgA, PkgB) is generated in
> base::namespaceImportFrom()
> [http://svn.r-project.org/R/trunk/src/library/base/R/namespace.R],
> simply because it detects that "foo" (=n) has already been imported to
> PkgC' namespace (=impenv):
>
> if (exists(n, envir = impenv, inherits = FALSE)) {
>     if (.isMethodsDispatchOn() && methods:::isGeneric(n, ns)) {
>         ## warn only if generic overwrites a function which
>         ## it was not derived from
>         ...
>     }
>     warning(sprintf(msg, sQuote(paste(nsname, n, sep = "::")),
> sQuote(from)), call. = FALSE, domain = NA)
> }
>
> Note how there is already code for avoiding "false" warnings on S4
> generic function.  This is what we'd like to have also for S3 generic
> functions, but it's much harder to test for such since they're not
> formally defined.  However, I'd argue that it is safe to skip the
> warning *when the variable to be imported does not actually exist in
> the package being imported* (e.g. when just rexported), i.e.
>
>>svn diff namespace.R
> Index: namespace.R
> ===================================================================
> --- namespace.R (revision 63776)
> +++ namespace.R (working copy)
> @@ -871,6 +871,10 @@
>      }
>      for (n in impnames)
>         if (exists(n, envir = impenv, inherits = FALSE)) {
> +            ## warn only if imported variable actually exists in the
> +            ## namespace imported from, which is not the case if
> +            ## the variable is rexported from another namespace
> +            if (!exists(n, envir = ns, inherits = FALSE)) next
>             if (.isMethodsDispatchOn() && methods:::isGeneric(n, ns)) {
>                 ## warn only if generic overwrites a function which
>                 ## it was not derived from

Ok, if import(PkgA, PkgB) and PkgB reexports a *different* foo() than
PkgA::foo(), say PkgZ::foo so identical(PkgB::foo, PkgA::foo) is
FALSE, then there is indeed a conflict.  An alternative patch:

>svn diff namespace.R
Index: namespace.R
===================================================================
--- namespace.R (revision 63776)
+++ namespace.R (working copy)
@@ -871,6 +871,11 @@
     }
     for (n in impnames)
        if (exists(n, envir = impenv, inherits = FALSE)) {
+            ## warn only if imported variable is non-identical to
+            ## the one already imported
+            getImp <- get(n, envir = impenv)
+            obj <- get(n, envir = ns)
+            if (identical(obj, getImp)) next
            if (.isMethodsDispatchOn() && methods:::isGeneric(n, ns)) {
                ## warn only if generic overwrites a function which
                ## it was not derived from

/Henrik

>
> I'm planning to propose ("wishlist / enhancement"; it may even be a
> bug) this over at https://bugs.r-project.org/.
>
> Comments, anyone?
>
> /Henrik
>
>
>> Paul
>>
>> Henrik Bengtsson <h...@biostat.ucsf.edu> wrote:
>>
>>>Hi,
>>>
>>>SETUP:
>>>Consider three packages PkgA, PkgB and PkgC.
>>>
>>>PkgA defines a generic function foo() and exports it;
>>>
>>>export(foo)
>>>
>>>PkgB imports PkgA::foo() and re-exports it;
>>>
>>>importFrom(PkgA, foo)
>>>export(foo)
>>>
>>>PkgC imports everything from PkgA and PkgB:
>>>
>>>imports(PkgA, PkgB)
>>>
>>>
>>>PROBLEM:
>>>Loading or attaching the namespace of PkgC will generate a warning:
>>>
>>>  replacing previous import by 'PkgA::foo' when loading 'PkgC'
>>>
>>>This in turn causes 'R CMD check' on PkgC to generate a WARNING (no-go at 
>>>CRAN):
>>>
>>>* checking whether package 'PkgC' can be installed ... WARNING
>>>Found the following significant warnings:
>>>  Warning: replacing previous import by 'PkgA::foo' when loading
>>>'CellularAutomaton'
>>>
>>>
>>>FALSE?
>>>Isn't it valid to argue that this is a "false" warning, because
>>>identical(PkgB::foo, PkgA::foo) is TRUE and therefore has no effect?
>>>
>>>
>>>/Henrik
>>>
>>>PS. The above can be avoided by using explicit importFrom() on PkgA
>>>and PkgB, but that's really tedious.  In my case this is out of my
>>>reach, because I'm the author of PkgA and PkgB but not many of the
>>>PkgC packages.
>>>
>>>______________________________________________
>>>R-devel@r-project.org mailing list
>>>https://stat.ethz.ch/mailman/listinfo/r-devel

______________________________________________
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel

Reply via email to