Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Value/reference equality for same named function in package/namespace environments?

Tags:

r

Lets grab the environments "namespace:stats" and "package:stats"

ns = getNamespace( "stats" )
pkg = as.environment( "package:stats" )

Now lets get the function "sd" in both:

nsSd = get( "sd" , envir = ns , inherits = FALSE )
pkgSd = get( "sd" , envir = pkg , inherits = FALSE )

Are they the same? They are! But what does "same" mean? Reference or value equality?

identical( nsSd , pkgSd )

This implies reference equality, since the following returns FALSE:

test1 = function() {}
test2 = function() {}
identical( test1 , test2 )

But if that's true, it means that an Environment's frame can contain function pointers alongside function objects. Further complicating the issue is fact that a function can "live" in one environment, but the function can be told that its executing environment is another environment. Chambers SoDA doesn't seem to have an answer (its a dense book, maybe I missed it!)

So, I'd like a definitive answer. Which of the following are correct? Or is there a false trichotomy here?

  1. nsSd and pkgSd are two different objects (albeit copies of each other), where the object in pkgSd has ns as its executing environment
  2. nsSd and pkgSd are pointers to the same object.
  3. nsSd is a pointer to pkgSd and as such they are treated as identical
like image 739
SFun28 Avatar asked Feb 14 '12 14:02

SFun28


2 Answers

They are pointers to the same object. Using this answer to another question, we can check if two objects refer to the same place in memory.

are_same <- function(x, y)
{
  f <- function(x) capture.output(.Internal(inspect(x)))
  all(f(x) == f(y))
}

are_same(nsSd, pkgSd) #TRUE
are_same(1:5, 1:5)    #FALSE
like image 150
Richie Cotton Avatar answered Nov 12 '22 19:11

Richie Cotton


This isn't mostly an answer to your main question. On that issue, though, I agree with Dirk: there is just one sd() function, and it can be accessed, depending on the circumstances, by different scoping paths. For instance, when you type sd(x) at the command line, the function corresponding to the name sd will be found via its entry in the frame of the package:stats environment. When you type stats:::sd(x), or when another function in stats package calls sd(x), it will be found via a search in the namespace:stats environment.


Instead, I just wanted to make the point that your example using test1() and test2() doesn't really imply anything about the "reference equality" of objects that do evaluate to identical. To see the real reason those two are not identical, have a look at their structure as revealed by str():

test1 <- function() {}
test2 <- function() {}
identical( test1 , test2 )
# [1] FALSE

str(test1)
# function ()  
#  - attr(*, "srcref")=Class 'srcref'  atomic [1:8] 1 13 1 25 13 25 1 1
#   .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x01613f54> 

str(test2)
# function ()  
#  - attr(*, "srcref")=Class 'srcref'  atomic [1:8] 1 13 1 25 13 25 1 1
#   .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x01615730> 

If you scroll over to the right side of the code box above, you will see that the two functions differ in one of their attributes, namely the environment associated with their source files. (I don't know much about that attribute, but that's not really relevant here. The point is that they're not identical!)

If you tell R that you don't want to keep sourcefile attribute data with every function that's created, the 'unexpected' behavior of identical(test1, test2) goes away:

options(keep.source=FALSE)
test1 <- function() {}
test2 <- function() {}
identical( test1 , test2 )
# [1] TRUE
like image 44
Josh O'Brien Avatar answered Nov 12 '22 20:11

Josh O'Brien