Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle assignment into an environment #20

Open
clarkfitzg opened this issue Oct 10, 2017 · 2 comments
Open

Handle assignment into an environment #20

clarkfitzg opened this issue Oct 10, 2017 · 2 comments

Comments

@clarkfitzg
Copy link
Contributor

In the following I'd like to know that the environment e was updated:

> code = quote(assign("x", 20, envir = e))
> CodeDepends::getInputs(code)
An object of class "ScriptNodeInfo"

...
Slot "outputs":
[1] "x"

Slot "updates":
character(0)

Slot "sideEffects":
character(0)
...

This came up attempting to analyze the body of the koRpus::set.kRp.env() function which sets a bunch of variables in a private package environment koRpus:::.koRpus.env.

@gmbecker
Copy link
Collaborator

gmbecker commented Oct 10, 2017

(now with proper formatting, stupid email reply...)

This is doable. We can make it the default (although environments are are
pass-by-reference so they violate assumptions we make all over the place
when, e.g., creating threads, tree-shaking, differentiating inputs vs
outputs/updates, etc).

The default assign() handler is assignfunhandler:

assignfunhandler = function(e, collector, basedir, input,
                            formulaInputs, update, pipe = FALSE,
                            nseval = FALSE, ...){
    if(is.symbol(e[[2]])) {
        warning("assign() used with symbol as first argument. Unable to
statically resolve what name the value will be assigned to")
        return()
    } else { ## character containing the name to assign to
        collector$calls("assign")
        if(is.character(e[[2]]))
           collector$set(e[[2]]) ##variable
        else
           collector$set(structure(as.character(NA), names =
deparse(e[[2]])))
        getInputs(e[[3]], collector = collector, basedir = basedir, input =
TRUE,
                  formulaInputs = formulaInputs, update = update, pipe =
pipe,
                  nseval = nseval, ... )
    }

}

We can add a check to see if the envir argument is set and if so, add it to
updates:

> code = "e = new.env(); assign('x', 5L, envir = e)"
> scr = readScript(txt = code)
> assignfunhandler2 = *<snip, see below>*
> col = inputCollector(assign = assignfunhandler2)
> getInputs(scr, col)
An object of class "ScriptInfo"
[[1]]
An object of class "ScriptNodeInfo"
Slot "files":
character(0)

Slot "strings":
character(0)

Slot "libraries":
character(0)

Slot "inputs":
character(0)

Slot "outputs":
[1] "e"

Slot "updates":
character(0)

Slot "functions":
new.env
  FALSE

Slot "removes":
character(0)

Slot "nsevalVars":
character(0)

Slot "sideEffects":
character(0)

Slot "code":
e = new.env()


[[2]]
An object of class "ScriptNodeInfo"
Slot "files":
character(0)

Slot "strings":
character(0)

Slot "libraries":
character(0)

Slot "inputs":
character(0)

Slot "outputs":
[1] "x"

*Slot "updates":*
*[1] "e"*

Slot "functions":
assign
 FALSE

Slot "removes":
character(0)

Slot "nsevalVars":
character(0)

Slot "sideEffects":
character(0)

Slot "code":
assign("x", 5L, envir = e)

We can think about making this the default, but you can experiment with the
behavior now, as above.

Here is the definition of assignhandler2 in a useful (cut/paste-able) form:

assignfunhandler2 = function(e, collector, basedir, input,
                            formulaInputs, update, pipe = FALSE,
                            nseval = FALSE, ...){
    if(is.symbol(e[[2]])) {
        warning("assign() used with symbol as first argument. Unable to
statically resolve what name the value will be assigned to")
        return()
    } else { ## character containing the name to assign to
        collector$calls("assign")
        if(is.character(e[[2]]))
           collector$set(e[[2]]) ##variable
        else
            collector$set(structure(as.character(NA), names =
deparse(e[[2]])))
        eind = numeric()
        if("envir" %in% names(e)) {
            eind = match("envir", names(e))
            collector$update(asVarName(e[[eind]]))
        }
        lapply(setdiff(seq(3, length(e)), eind), function(i) {
            getInputs(e[[i]], collector = collector, basedir = basedir,
input = TRUE,
                  formulaInputs = formulaInputs, update = update, pipe =
pipe,
                  nseval = nseval, ... )})
      }

}

--
Gabriel Becker, PhD
Associate Scientist (Bioinformatics)
Genentech Research

@clarkfitzg
Copy link
Contributor Author

Thanks @gmbecker, I'll use this. I imagine the pass by reference semantics complicates things significantly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants