From Mark.Bravington at csiro.au  Wed Oct 29 23:22:47 2014
From: Mark.Bravington at csiro.au (Mark.Bravington at csiro.au)
Date: Wed, 29 Oct 2014 22:22:47 +0000
Subject: [Rd] Unexpected behavior of identical() with language objects
In-Reply-To: <CAFOpNVEnue7m5yViF6PzQ7jixZgykTZ+-h3+BxSm0Xz5JWtECw@mail.gmail.com>
References: <CAFOpNVGxxqH0uYjouj6rG9b9LdbigQQbNDWgvS8NxhYnn61F8A@mail.gmail.com>
	<CAPPM_gQJFLHPs__OwVyNuftkWz2X7+sn_LwRzfpgrT6qdg37nw@mail.gmail.com>
	<CABdHhvGtKp4-J6G--aJ1+Y-rNbugOtW0kzjuevvm_EdQTANnJw@mail.gmail.com>
	<CAFOpNVEnue7m5yViF6PzQ7jixZgykTZ+-h3+BxSm0Xz5JWtECw@mail.gmail.com>
Message-ID: <1D2694C7C3A6C04AA75E11C592A882E44F8EC38B@exmbx05-cdc.nexus.csiro.au>

[See below for full email trail-- Outlook has beaten me into submission]
> I ran into this and found the result very surprising:

> identical( quote({ a }),  quote({ a }) ) # FALSE
> <<...>>
> -Winston

> > Yes, looks like srcrefs are to blame:
> >
> > x <- quote({ a })
> > y <- quote({ a })
> >
> > identical(x, y)
> > # [1] FALSE
> <<...>>
> > Maybe identical() needs an ignore.srcref option? Normally when
> > comparing expressions or functions, you want to compare the code, not
> > it's textual representation.
> >
> > Hadley

What a great gotcha!

Seems to me it would be better to leave 'identical' alone and have a wrapper that you can call to strip any srcrefs. There is already 'utils::removeSource' but it doesn't work as-is with non-functions. The 5-minute hack below solves the particular example, but may well fall over with other cases--- not tested.

(This sort of thing reinforces my own feelings about 'srcref' as opposed to nice simple 'source'...)

Mark Bravington
CSIRO/Marine Lab/Hobart/Tas 7000/Australia

rmsrc <- function (fn) {
# based on utils::removeSource
    is.fun <- is.function( fn)
    if( (!is.fun && !is.language(fn)) || is.primitive(fn)) {
return(fn)
    }
    
    attr(fn, "source") <- NULL
    attr(fn, "srcref") <- NULL

    if( !is.fun) {
      fn <- as.function( list( fn))
    }

    attr(body(fn), "wholeSrcref") <- NULL
    attr(body(fn), "srcfile") <- NULL
    recurse <- function(part) {
        attr(part, "srcref") <- NULL
        if (is.language(part) && is.recursive(part)) {
            for (i in seq_along(part)) part[[i]] <- recurse(part[[i]])
        }
        part
    }
    body(fn) <- recurse(body(fn))
    
    if( !is.fun) {
return( body( fn))
    } else {
return( fn)
    }
}
#> identical( rmsrc( quote({a})), rmsrc( quote({a})))
#[1] TRUE 

> -----Original Message-----
> From: r-devel-bounces at r-project.org [mailto:r-devel-bounces at r-project.org]
> On Behalf Of Winston Chang
> Sent: Thursday, 30 October 2014 7:59 AM
> To: Hadley Wickham
> Cc: R Devel List
> Subject: Re: [Rd] Unexpected behavior of identical() with language objects
> 
> Ah, I was using identical() to compare two function bodies. It returns
> FALSE even when you remove srcrefs from the body:
> 
> f1 <- function(x) {
>   if (TRUE) { x }
> }
> f2 <- function(x) {
>   if (TRUE) { x }
> }
> f1b <- body(f1)
> f2b <- body(f2)
> attributes(f1b) <- NULL
> attributes(f2b) <- NULL
> 
> # The bodies look the same with str()
> str(f1b)
> #  language {  if (TRUE) {; x; } }
> str(f2b)
> #  language {  if (TRUE) {; x; } }
> 
> identical(f1b, f2b)
> # FALSE
> 
> 
> 
> What I didn't realize was that the curly brace inside the body also
> independently captures srcrefs, but this isn't printed with str(f1b).
> However, str() on a more targeted part of the object reveals them:
> str(f1b[[2]][[3]])
> # length 2 {  x }
> #  - attr(*, "srcref")=List of 2
> #   ..$ :Class 'srcref'  atomic [1:8] 2 13 2 13 13 13 2 2
> #   .. .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile'
> <environment: 0x452b2c0>
> #   ..$ :Class 'srcref'  atomic [1:8] 2 15 2 15 15 15 2 2
> #   .. .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile'
> <environment: 0x452b2c0>
> #  - attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment:
> 0x452b2c0>
> #  - attr(*, "wholeSrcref")=Class 'srcref'  atomic [1:8] 1 0 2 17 0 17 1 2
> #   .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile'
> <environment: 0x452b2c0>
> 
> -Winston
> 
> On Wed, Oct 29, 2014 at 3:46 PM, Hadley Wickham <h.wickham at gmail.com>
> wrote:
> >>> Is this expected behavior? I can't seem to find anything in the help
> >>> for identical that relates to this.
> >>>
> >> It's not in ?identical, but ?Paren gives you some pointers.
> >> str(quote((a))) and str(quote({a})) are also informative.
> >
> > Yes, looks like srcrefs are to blame:
> >
> > x <- quote({ a })
> > y <- quote({ a })
> >
> > identical(x, y)
> > # [1] FALSE
> >
> > attr(x, "srcref") <- NULL
> > attr(x, "srcfile") <- NULL
> > attr(x, "wholeSrcref") <- NULL
> >
> > attr(y, "srcref") <- NULL
> > attr(y, "srcfile") <- NULL
> > attr(y, "wholeSrcref") <- NULL
> > identical(x, y)
> > # [1] TRUE
> >
> > Maybe identical() needs an ignore.srcref option? Normally when
> > comparing expressions or functions, you want to compare the code, not
> > it's textual representation.
> >
> > Hadley
> >
> > --
> > http://had.co.nz/
> 
> ______________________________________________
> R-devel at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel



[Rd] Unexpected behavior of identical() with language objects
Mark.Bravington at csiro.au