Skip to content

[R-pkg-devel] Visible bindings and reference classes

7 messages · Martin Maechler, Colin Gillespie, William Dunlap +1 more

#
Dear All,

I have a package that uses reference classes. When I build the package I
get numerous notes of the sort

Note: no visible binding for '<<-' assignment to 'pars'

I've tried using GlobalVariables, but that didn't solve the issue.

After some googling, I came across the page
http://stackoverflow.com/q/23475309/203420 which suggests

suppressBindingNotes <- function(variablesMentionedInNotes) {
  for(variable in variablesMentionedInNotes) {
    assign(variable,NULL, envir = .GlobalEnv)
  }
}
suppressBindingNotes(c("dat", "internal", "xmin", "pars", "no_pars"))

But checking the package with --as-cran raises the note

* checking R code for possible problems ... NOTE
Found the following assignments to the global environment:
  File ?poweRlaw/R/aaa_all_classes.R?:
  assign(variable, NULL, envir = .GlobalEnv)

What is the correct way of removing the "visible bindings" notes?

Thanks

Colin
#
[ You mean globalVariables(): and it's a bad idea anyway IMO, 
  even if it is recommended : If you declare a variable in
  there, it is "global" in all places in your package and the
  codetools won't report it anywhere anymore.
  Much better in my view is to use something like

  var7 <- NULL # ~= globalVariables("var7")
 
]

To your question:

Reference classes are used in *many* places,  and the use of  ' <<- '
is really "standard" there.
e.g., package 'lme4', or 'pcalg' are two packages I'm involved with,
which use ref.classes and ' <<- '  but are "fine" with that.

So there must be something peculiar in your package leading to
the  <<-  warnings.

Maybe you should look into the source code of such other CRAN
packages to see how "they" do it differently than you.

Best regards,
Martin

Martin Maechler, ETH Zurich
#
After a bit more investigating I've narrowed it down.  The notes can be
generated by having

d1 = setRefClass("d1", fields=list(x = "numeric"))
d1$accessors("x")

**and** having ByteCompile: true. Commenting out the accessors line removes
the visible binding note. The  x=NULL  or globalVariables("x") trick
doesn't work.

A stackoverflow answer suggests that I need to add

assign(variable,NULL, envir = .GlobalEnv)

but this generates a new NOTE under R CMD check

I've not been able to find another package that uses accessors and
ByteCompile

Thanks

Colin

  
  
#
This is a problem in the compiler package.  Here is a way to reproduce it:

def <- quote(Config <- setRefClass("Config",
    fields = list(
        ConfigString = "character"),
    methods = list(
        # Constructor
        initialize = function() {
            ConfigString <<- "Hello, World!"
        })
))
dput(def, file= (tf <- tempfile()))
compiler::cmpfile(tf)
#Note: no visible binding for '<<-' assignment to 'ConfigString'
saving to file
#"C:\Users\wdunlap\AppData\Local\Temp\Rtmpk91qvT\file26d447c45b81.Rc" ...
done




Bill Dunlap
TIBCO Software
wdunlap tibco.com

On Tue, Aug 11, 2015 at 4:24 AM, Martin Maechler <maechler at stat.math.ethz.ch

  
  
#
You can avoid the temporary file by replacing
  dput(def, file= (tf <- tempfile()))
  compiler::cmpfile(tf)
with
  cdef <- compiler::compile(def)
  #Note: no visible binding for '<<-' assignment to 'ConfigString'
The compiled code appears to work.
  eval(cdef)
  c1 <- Config$new()
  c1
  #Reference class object of class "Config"
  #Field "ConfigString":
  #[1] "Hello, World!"
  > objects(all=TRUE)
  #[1] ".__C__Config"           ".__global__"
  #[3] ".requireCachedGenerics" "c1"
  #[5] "cdef"                   "Config"
  #[7] "def"


Bill Dunlap
TIBCO Software
wdunlap tibco.com
On Tue, Aug 11, 2015 at 8:47 AM, William Dunlap <wdunlap at tibco.com> wrote:

            

  
  
#
On Tue, 11 Aug 2015, Colin Gillespie wrote:

            
For now I would recommend not enabling byte code compilation or not
using accessors if you want to avoid these warnings. The compiler
notes are valid given the implementation of the accessors mechanism;
I'm fairly sure some minor changes to the accessor mechanism could
address this. We'll look into this.

Best,

luke

  
    
#
On Tue, 11 Aug 2015, William Dunlap wrote:

            
Short answer: this isn't actually what is happening in Colin's
example, and in any case the notes seem OK to me, given what is
happening.

For the long answer, the basic issue is that the compiler, and
codetools, look at the code as it is written and try to make sense of
it. The methods implementation, and reference class implementation in
particular, rewrite the code in various ways. This creates some
mismatches. These can either we worked around by adapting
compiler/codetools to internals of methods/reference classes, or by
modifying methods/reference class implementations to be more amenable
to static analysis, or some combination.

The way packages are compiled already does adapt to the implementation
of methods/reference classes/etc to some degree by the way it compiles
code. It does not compile at the file level. Instead it compiles while
serializing code that has been loaded and in some cases modified by
the methods infrastructure, which is why the expression/file compile
example isn't related to what Colin was seeing. A reference class
definition like this at package top level should not be a problem for
byte compilation.

Here is a slight modification of Colin's example:

d1 = setRefClass("d1", fields=list(x = "numeric"))
a <- d1$accessors("x")

Then
function () 
x
<environment: 0x103c7e1f8>

and the environment of this function does not contain "x":
<anonymous>: no visible binding for global variable ?x?

When a packages using accessors is byte compiled the compiler sees
these incomplete accessor functions and points this out.

For an instance,
Class method definition for method getX()
function () 
x
<environment: 0x103ae3378>

This function has a new environment, the .xData part of v, and that does contain "x":
# no note

So a change to the implementation of the accessors method to create
its accessors with an environment that is like the one they would see
when the are used would remove this note. I'm happy to help but
someone more familiar with reference class internals would have to
take the lead on this.

Compiled functions revert to interpreted when their environments are
changed, so the code does work, but doesn't benefit from compilation.
This is a bit overly cautious: if the structure of the environments in
terms of the variables they define remains the same, as it would if
the accessors created in the class had environments that matched their
intended usage, then the compiled code would remain valid and could
still be used. [Of course at present there isn't much if any benefit
to compiling functions this small, but that may change in the future.]