[Rd] Unexpected behavior of identical() with language objects

Duncan Murdoch murdoch.duncan at gmail.com
Wed Oct 29 23:41:38 CET 2014


On 29/10/2014, 6:22 PM, Mark.Bravington at csiro.au wrote:
> [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.

Sounds like utils::removeSource may need to be fixed, but until today
I'd never heard any complaints about it.

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

Nice simple 'source' can't do what srcrefs can do, for example report on
locations during debugging, or when run-time errors occur.

Duncan Murdoch

> 
> 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
> 
> ______________________________________________
> R-devel at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
>



More information about the R-devel mailing list