[Rd] :: and ::: as .Primitives?

Philippe GROSJEAN Philippe.GROSJEAN at umons.ac.be
Fri Jan 23 18:14:59 CET 2015


I tend to use this (in my own internal code *only*):

exported <- function (pkg) {
	if (pkg == "base") {
		function (fun) {
			fun <- as.character(substitute(fun))
			res <- .BaseNamespaceEnv[[fun]]
			if (is.null(res))
				stop(fun, " is not found in package base")
			res
		}
	} else {
		ns <- getNamespace(pkg)
		exports <- getNamespaceInfo(ns, "exports")
		function (obj) {
			obj <- as.character(substitute(obj))
			exportedObj <- exports[[obj]]
			if (is.null(exportedObj)) {
				if (is.null(ns[[obj]])) {
					stop(obj, " does not exists in package ", pkg)	
				} else {
					stop(obj, " is not exported from package ", pkg)
				}
			}
			ns[[exportedObj]]
		}
	}
}
stats <- exported("stats")
stats(acf)
stats("[.acf")
stats("inexistant")
exported("base")(ls)
exported("base")(inexistant)

## Performance tests for what it’s worth
microbenchmark::microbenchmark(stats::acf, (stats <- exported("stats"))(acf), stats(acf))
microbenchmark::microbenchmark(base::ls, (base <- exported("base"))(ls), base(ls), .BaseNamespaceEnv$ls)

So, `::` is slow and I can get better speed results thanks to binding both the namespace and the exports environments in the `stats` closure. Unless I miss something, this is not much a problem for base package that is never unloaded. Yet, .BaseNamespaceEnv$xxx, or baseenv()$xxx does the job faster and simpler. 

However, there is a vicious problem with my exported() function, which is, to say the least, dangerous under the hand of unaware users. Indeed:

stats <- exported(“stats”)

creates a new binding to both the namespace and the exports environments of the stats package. So, if I do:

detach(“package:stats”, unload = TRUE), then library(“stats”), I got two versions of the package in memory, and my `stats`closure refers to an outdated version of the package. This is particularly problematic if the package was recompiled in between (in the context of debugging).

Conclusion: much of the lost of performance in `::` is due to not caching the environments. This is fully justified to keep the dynamism of the language at full power and to avoid a messy state of R as described here above… Regarding dynamism, even `stats::acf`remains discutable.

Moreover, it is possible to do many other crazy things with these environments, once one got a grip on them. So, even getNamespace() and getNamespaceInfo() are dangerous. Perhaps this should be emphasised in the ?getNamespace man page?

This is also why the code above is not released in the wild… Well, now it is :-(

Best,

Philippe

..............................................<°}))><........
 ) ) ) ) )
( ( ( ( (    Prof. Philippe Grosjean
 ) ) ) ) )
( ( ( ( (    Numerical Ecology of Aquatic Systems
 ) ) ) ) )   Mons University, Belgium
( ( ( ( (
..............................................................

> On 23 Jan 2015, at 16:01, luke-tierney at uiowa.edu wrote:
> 
> On Thu, 22 Jan 2015, Michael Lawrence wrote:
> 
>> On Thu, Jan 22, 2015 at 11:44 AM,  <luke-tierney at uiowa.edu> wrote:
>>> 
>>> For default methods there ought to be a way to create those so the
>>> default method is computed at creation or load time and stored in an
>>> environment.
>> 
>> We had considered that, but we thought the definition of the function
>> would be easier to interpret if it explicitly specified the namespace,
>> instead of using tricks with environments. The same applies for
>> memoizing the lookup in front of a loop.
> 
> interpret in what sense (human reader or R interpreter)? In either
> case I'm not convinced.
> 
>> The implementation of these functions is almost simpler in C than it
>> is in R, so there is relatively little risk to this change. But I
>> agree the benefits are also somewhat minor.
> 
> I don't disagree, but it remains that even calling the C version has
> costs that should not need to be paid. But maybe we can leave that to
> the compiler/byte code engine. Optimizing references to symbols
> resolved statically to name spaces and imports is on the to do list,
> and with a little care that mechanism should work for foo::bar uses as
> well.
> 
> Best,
> 
> luke
> 
>> 
>>> For other cases if I want to use foo::bar many times, say
>>> in a loop, I would do
>>> 
>>> foo_bar <- foo::bar
>>> 
>>> and use foo_bar, or something along those lines.
>>> 
>>> When :: and ::: were introduce they were intended primarily for
>>> reflection and debugging, so speed was not an issue. ::: is still
>>> really only reliably usable that way, and making it faster may just
>>> encourage bad practice. :: is different and there are good arguments
>>> for using it in code, but I'm not yet seeing good arguments for use in
>>> ways that would be performance-critical, but I'm happy to be convinced
>>> otherwise. If there is a need for a faster :: then going to a
>>> SPECIALSXP is fine; it would also be good to make the byte code
>>> compiler aware of it, and possibly to work on ways to improve the
>>> performance further e.g. through cacheing.
>>> 
>>> Best,
>>> 
>>> luke
>>> 
>>> 
>>> On Thu, 22 Jan 2015, Peter Haverty wrote:
>>> 
>>> 
>>>> Hi all,
>>>> 
>>>> When S4 methods are defined on base function (say, "match"), the
>>>> function becomes a method with the body "base::match(x,y)". A call to
>>>> such a function often spends more time doing "::" than in the function
>>>> itself.  I always assumed that "::" was a very low-level thing, but it
>>>> turns out to be a plain old function defined in base/R/namespace.R.
>>>> What would you all think about making "::" and ":::" .Primitives?  I
>>>> have submitted some examples, timings, and a patch to the R bug
>>>> tracker (https://bugs.r-project.org/bugzilla3/show_bug.cgi?id=16134).
>>>> I'd be very interested to hear your thoughts on the matter.
>>>> 
>>>> Regards,
>>>> Pete
>>>> 
>>>> ____________________
>>>> Peter M. Haverty, Ph.D.
>>>> Genentech, Inc.
>>>> phaverty at gene.com
>>>> 
>>>> ______________________________________________
>>>> R-devel at r-project.org mailing list
>>>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>>> 
>>> 
>>> --
>>> Luke Tierney
>>> 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-tierney at uiowa.edu
>>> Iowa City, IA 52242                 WWW:  http://www.stat.uiowa.edu
>>> 
>>> 
>>> ______________________________________________
>>> R-devel at r-project.org mailing list
>>> https://stat.ethz.ch/mailman/listinfo/r-devel
>> 
> 
> -- 
> Luke Tierney
> 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-tierney at uiowa.edu
> Iowa City, IA 52242                 WWW:  http://www.stat.uiowa.edu
> 
> ______________________________________________
> R-devel at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel



More information about the R-devel mailing list