[R] How to modify object's code living in some environment?

Duncan Murdoch murdoch@dunc@n @end|ng |rom gm@||@com
Wed Dec 29 11:24:16 CET 2021


On 28/12/2021 4:21 p.m., Grzegorz Smoliński wrote:
> Thank you for all the comments. If this is not a problem, I would like
> to continue this thread as for me a lot of questions are still open.
> But if I should post other questions in different messages / topics,
> please let me know.
> 
> I didn’t answer Ivan’s question previously, but yes, my desired result
> is indeed what debug() / debugonce() does and Ivan mentioned trace()
> earlier also, so I have tried this. I thought that trace() will be
> better in my case (than debug()) since I would like to be able to call
> trace() from one environment and insert browser() (using trace()) to
> the function defined in parent environment (still not sure if I
> understand this concept of environments).
> 
> Even if I would like to use it in an shiny app, I have started with
> this example:
> 
> ------------------------------
> 
> fun1 <- function() {
>    a <-  1
>    a
> }
> 
> fun2 <- function() {
>    fun1 <- function(){
>      b <- 2
>      b
>    }
>    trace(fun1, browser, where = globalenv())
> }
> 
> fun1()
> fun2()
> fun1()
> 
> --------------------------------
> 
> So I was hoping to use trace inside child environment of global env,
> i.e. set browser() using trace() to the body of function defined in
> parent environment, but for sure I do not understand this since
> trace() in my example above is putting the browser() function inside
> fun1() function defined not in global environment, but in the body of
> fun2(). I.e. I'm starting to browse the fun1() function with the local
> variable 'b', not 'a' as I wanted.

You are being bitten by normal R scoping.  While fun2 is executing, as 
soon as it executes the fun1 assignment the name fun1 is bound to the 
local function, so when you use fun1 in the first argument to trace(), 
you get that one.  It will ignore then "where" argument because it was 
passed a function object.  If you quote the name it will have to do a 
lookup and it will use "where".  So write fun2 like this:

fun2 <- function() {
   fun1 <- function(){
     b <- 2
     b
   }
   trace("fun1", browser, where = globalenv())
}


> 
> Can I ask for help with this? In other words, how can I refer to some
> function from the parent environment (not necessarily defined in the
> global environment) being in the child environment?

You could have used parent.frame() in place of globalenv() if you want a 
function that was visible to the caller of fun2.  If you want a function 
that was defined with the same environment as fun2, you can usually use 
environment(fun2), or parent.env(environment()).


> 
> Also, if I could ask - is the reversed operation possible? I mean -
> can I refer to some function from the child environment being in the
> parent environment? I guess not, because of ephemerality?

Sometimes you can, but not usually.  If fun2 had returned a value that 
referenced the evaluation frame, you would be able to see the "b" 
version of fun1 there.  But yours didn't.  This one would:


fun2 <- function() {
   fun1 <- function(){
     b <- 2
     b
   }
   trace("fun1", browser, where = globalenv())
   environment()
}

Now if you do e <- fun2(), you'll set the trace on the global fun1, but 
e$fun1 will be the local one.

I don't really know how Shiny sets things up, so I can't help with the 
stuff below.

Duncan Murdoch


> 
> As I mentioned, I'm thinking about all of this in the context of shiny
> app and the shiny app which consists of multiple files, modules. I'm
> thinking if I could refer to any function from any environment (or at
> least to any function in any parent environment) from one place (one,
> current environment) where I will call debug() or trace() to insert
> browser() at the beginning of this chosen function. And by "any
> function" I'm thinking about another thing problematic for me. If I
> consider this example:
> 
> -------------------------------
> mod.R inside R/
> ---
> 
> mod_UI <- function(id) {
> }
> 
> name_mod <- function(input, output, session) {
>    fun1 <- reactive({
>      a  <-  1
>    })
> }
> 
> ---
> app.R
> ---
> 
> library(shiny)
> 
> ui <- fluidPage(
>    mod_UI("mod"),
>    textOutput("env")
> )
> 
> server <- function(input, output, session) {
> 
>    name_mod("mod")
> 
>    output$env <- renderPrint({
>      names(environment(name_mod))
>    })
> 
>    observe({
>      #trace(fun1, browser, where = environment(name_mod))
>    })
> }
> 
> shinyApp(ui, server)
> ---------------------------------------
> I tried to refer to the fun1 in environment(name_mod) - if I'm not
> wrong, this is my parent environment, where objects "mod_UI" and
> "name_mod" exist, but obviously I'm doing this wrong, right? Because
> here: "#trace(fun1, browser, where = environment(name_mod))" I have
> tried to refer to the module environment, but I should refer to the
> environment inside "name_mod". I don't know how to do this and here I
> also would like to ask for help.
> 
> I know this may not be the best place for questions regarding shiny,
> but I think these questions are purely linked to environment topics.
> 
> I also remember Bert's post about "body()" and yes, this seems to be a
> better idea than my first try in my first post, but "trace()" and
> "debug()" seem much easier as all I want is to insert browser(). But I
> just can't get it, how to refer to functions in (any) other
> environments.
> 
> Thank you very much for all your help and I hope it is OK to keep asking :).
> 
> pon., 27 gru 2021 o 18:28 Duncan Murdoch <murdoch.duncan using gmail.com> napisał(a):
>>
>> On 27/12/2021 8:25 a.m., Duncan Murdoch wrote:
>>> On 27/12/2021 8:06 a.m., Grzegorz Smoliński wrote:
>>>> Hi,
>>>>
>>>> I know it is possible to find the environment in which some object
>>>> lives using the 'environment()' function and the name of this object,
>>>> but how to modify code of this object after this? Below is MRE:
>>>
>>> You are misunderstanding the relation between environments and
>>> functions.  However, your understanding is also being messed up by a bug
>>> in R, so it's not entirely your fault.
>>
>> Actually this isn't a bug in R, it is working as documented.  For a
>> detailed explanation, see the response to my bug report here:
>> https://bugs.r-project.org/show_bug.cgi?id=18269 .
>>
>> For a quick idea:  "complex assignments" are assignments where there is
>> a complex expression on the left hand side, e.g.
>>
>>     environment(test)$test <- ...
>>
>> The way these are documented to work (in the R Language Definition
>> manual) makes intuitive sense when you are working with regular R
>> objects, but environments are "mutable" objects:  assigning them to a
>> new name doesn't make a new copy, just a new reference.  That causes the
>> definition of the complex assignment above to work in an unintuitive way.
>>
>> Conclusion:  you should avoid using calls like environment(f) on the
>> left hand side of assignments, especially as part of a larger
>> expression.  Break up the statement into steps like I did below:
>>
>>>      e <- environment(test)
>>>      e$test <- eval(parse(text = "function() 2"))
>>
>> The same advice would apply to a function that returned an R6 object or
>> a mutable object from the methods package (which are really environments
>> in disguise), as well as some other exotic objects.
>>
>> Duncan Murdoch
> 
> ______________________________________________
> R-help using r-project.org mailing list -- To UNSUBSCRIBE and more, see
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
> and provide commented, minimal, self-contained, reproducible code.



More information about the R-help mailing list