ywx
ywx

Reputation: 147

Difference between function objects in R

Suppose I create a list of functions in my R workspace, the same set of functions are also in a R file and after source(), the sourced function object should be identical to the corresponding function in the list I created, but this is not the case.

Example:

The f.R file contains f <- function(x) x^2.

In R console:

lst <- list(f=function(x) x^2)
source("f.R")
> ls()
[1] "f"    "lst"
> identical(f,lst$f)
[1] FALSE
> str(f)
function (x)
- attr(*, "srcref")=Class 'srcref'  atomic [1:8] 1 6 1 20 6 20 1 1
.. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x1b2fd60>
> str(lst$f)
function (x)
- attr(*, "srcref")=Class 'srcref'  atomic [1:8] 1 16 1 30 16 30 1 1
.. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x1bb4b50>

I also tried:

> identical(f,lst$f, ignore.environment=TRUE)
[1] FALSE
> all.equal(f,lst$f)
[1] TRUE

Why is that?

EDIT:

In general:

f <- function(x) x^2
g <- function(x) x^2
identical(f,g)
[1] FALSE

Why would the attributes of f and g be different? Does this suggest one should never use identical to test equality between function objects ?

Upvotes: 1

Views: 501

Answers (1)

Spacedman
Spacedman

Reputation: 94192

You've proved this yourself:

> str(f)
function (x)
- attr(*, "srcref")=Class 'srcref'  atomic [1:8] 1 6 1 20 6 20 1 1
.. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x1b2fd60>
> str(lst$f)
function (x)
- attr(*, "srcref")=Class 'srcref'  atomic [1:8] 1 16 1 30 16 30 1 1
.. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x1bb4b50>

These attributes are not identical, therefore the objects are not identical.

More info about these attributes is here:

What/Where are the attributes of a function object?

These attributes allow R to refer to the function's source code. Your two functions have been defined in different places, and so their source code references (such as which file they were defined in) are different.

The easiest difference to see is the srcfile attribute of the srcref:

> attr(attr(f,"srcref"),"srcfile")
f.R 
> attr(attr(lst$f,"srcref"),"srcfile")

>

Your f function is defined in f.R, so R keeps that in the attribute. Your lst$f is defined at the command line, so has a blank srcfile attribute.

Other parts of the attribute are also different because of how the code is parsed.

Specifically, the srcfile environment attribute is different:

> f <- function(x) x^2
> g <- function(x) x^2
> identical(f,g)
[1] FALSE
> print.default(attr(attr(g,"srcref"),"srcfile"))
<environment: 0x5b9fe40>
attr(,"class")
[1] "srcfilecopy" "srcfile"    
> print.default(attr(attr(f,"srcref"),"srcfile"))
<environment: 0x5ba1028>
attr(,"class")
[1] "srcfilecopy" "srcfile"    

I would say testing for identicality between function objects is probably not a good thing to do. The only time I can reliably believe two functions to be identical is if it has been created by assignment from the other one, eg: f2 = f1 implies identical(f1,f2).

Any other way of creating a function is prone to differences in srcref, enclosing environment, and even writing things with or without brackets.

Note all this is avoided if you set keep.source to FALSE.

> options(keep.source=TRUE)
> f <- function(x) x^2
> g <- function(x) x^2
> identical(f,g)
[1] FALSE
> options(keep.source=FALSE)
> f <- function(x) x^2
> g <- function(x) x^2
> identical(f,g)
[1] TRUE

But I still think you need to think very carefully about what constitutes identical functions for your purposes.

As for the ignore.environment argument, that ignores the environment in which the function was created. I'll write a function that creates functions to illustrate this:

foo creates an "adder" function that adds x to its argument:

> foo=function(x){print(x);function(z){z*x}}

So I can make an add-1 function and an add-2 function:

> f1 = foo(1)
[1] 1
> f2 = foo(2)
[1] 2

These functions are storing the local x in their environment. They are not identical by default:

> identical(f1,f2)
[1] FALSE

But are identical if you ignore the environment in which they were created:

> identical(f1,f2,ignore.environment=TRUE)
[1] TRUE

If you print them, they look identical (and I've set options(keep.source=FALSE) already) except for the environment hex-code reference:

> f1
function (z) 
{
    z * x
}
<environment: 0x8e4ea54>
> f2
function (z) 
{
    z * x
}
<environment: 0x8e4fb5c>

That's the environment ignored by ignore.environment.

Upvotes: 6

Related Questions