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

Duncan Murdoch murdoch@dunc@n @end|ng |rom gm@||@com
Wed Dec 29 15:49:03 CET 2021


On 29/12/2021 9:29 a.m., Grzegorz Smoliński wrote:
> Thank you. I posted this question on SO in the meantime as well, but
> there is no answer for my question, so I pasted your answer
> (https://stackoverflow.com/questions/70517625/how-to-use-trace-for-function-in-parent-or-child-environment).
> Hope it is fine for you.

No problem, though I'd suggest posting the answer as an answer, rather 
than an edit to your question.

Duncan Murdoch

> 
> śr., 29 gru 2021 o 11:24 Duncan Murdoch <murdoch.duncan using gmail.com> napisał(a):
>>
>> 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.
>>
> 
> ______________________________________________
> 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