[R] environments: functions within functions

Duncan Murdoch murdoch@dunc@n @end|ng |rom gm@||@com
Fri May 26 19:19:43 CEST 2023


I agree with Iris:  the switch() solution looks like the best option 
here.  The only change I'd make is to pass the dots down to the print 
function (or possibly warn about using them if those functions don't 
support any other parameters).

Duncan Murdoch

On 25/05/2023 8:20 p.m., Iris Simmons wrote:
> Hi,
> 
> 
> I think there are two easy ways to fix this. The first is to use a `switch`
> to call the intended function, this should not be a problem since there are
> a small number of print functions in **mixR**
> 
> ```R
> print.mixfitEM <- function (x, digits = getOption("digits"), ...)
> {
>      switch(x$family,
>      gamma   = printgamma  (x, digits),
>      lnorm   = printlnorm  (x, digits),
>      normal  = printnormal (x, digits),
>      weibull = printweibull(x, digits),
>      stop(gettextf("invalid '%s' value", "x$family", domain = "R")))
>      invisible(x)
> }
> environment(print.mixfitEM) <- getNamespace("mixR")
> print.mixfitEM <- compiler::cmpfun(print.mixfitEM)
> ```
> 
> This is nice because 'x' is no longer evaluated twice (you could try this
> yourself with something like
> `mixR:::print.mixfitEM(writeLines("testing"))`, you'll see the output
> twice, once for `x$family` and a second for evaluating `match.call()`
> expression), it follows standard evaluation, and 'x' is returned invisibly
> at the end, like most other `print` methods. If you really wanted to
> continue using `eval`, you could instead do something like
> 
> ```R
> print.mixfitEM <- function (x, digits = getOption("digits"), ...)
> {
>      expr <- quote(printfunction(x, digits))
>      expr[[1L]] <- as.symbol(paste0("print", x$family))
>      eval(expr)
>      invisible(x)
> }
> environment(print.mixfitEM) <- getNamespace("mixR")
> print.mixfitEM <- compiler::cmpfun(print.mixfitEM)
> ```
> 
> This also solves the same issues, but it's ugly and slower.
> 
> At least for now, I would copy one of the functions above into the
> site-wide startup profile file or your user profile, along with
> 
> ```R
> utils::assignInNamespace("print.mixfitEM", print.mixfitEM, "mixR")
> ```
> 
> This does have the unfortunate side effect of loading **mixR** every time
> an R session is launched, but you could also put it inside another function
> like:
> 
> ```R
> fix.mixR.print.mixfitEM <- function ()
> {
>      print.mixfitEM <- function(x, digits = getOption("digits"), ...) {
>          switch(x$family,
>          gamma   = printgamma  (x, digits),
>          lnorm   = printlnorm  (x, digits),
>          normal  = printnormal (x, digits),
>          weibull = printweibull(x, digits),
>          stop(gettextf("invalid '%s' value", "x$family", domain = "R")))
>          invisible(x)
>      }
>      environment(print.mixfitEM) <- getNamespace("mixR")
>      print.mixfitEM <- compiler::cmpfun(print.mixfitEM)
>      utils::assignInNamespace("print.mixfitEM", print.mixfitEM, "mixR")
> }
> ```
> 
> which you would then call in your scripts before using **mixR**. I hope
> this helps!
> 
> On Thu, May 25, 2023 at 10:19 AM Sarah Goslee <sarah.goslee using gmail.com>
> wrote:
> 
>> Hi,
>>
>> I ran into a problem with S3 method dispatch and scoping while trying
>> to use functions from the mixR package within my own functions. I know
>> enough to find the problem (I think!), but not enough to fix it
>> myself. The problem isn't really a package-specific problem, so I'm
>> starting here, and will file an issue with the maintainer once I have
>> a solution.
>>
>> Detailed explanation below, but briefly, the S3 methods in this
>> package use match.call() and then eval() to select the correct
>> internal method. This works fine from the command line, but if the
>> method is called from within another function, the use of
>> environment() within eval() means that the objects passed to the
>> wrapper function are no longer visible within the eval() call.
>>
>> I have a two-part question:
>> A. How do I get around this right now?
>> B. What would the correct approach be for the package authors?
>>
>> library(mixR)
>>
>> # first example from ?mixfit
>> ## fitting the normal mixture models
>> set.seed(103)
>> x <- rmixnormal(200, c(0.3, 0.7), c(2, 5), c(1, 1))
>> data <- bin(x, seq(-1, 8, 0.25))
>> fit1 <- mixfit(x, ncomp = 2)  # raw data
>> rm(x, data)
>> ###
>>
>> # simple function
>> funworks <- function(x) {
>>      print(x)
>> }
>>
>> ###
>>
>> # almost identical simple function
>> funfails <- function(thisx) {
>>      print(thisx)
>> }
>>
>> ###
>>
>> funworks(fit1)
>> funfails(fit1)
>>
>> #######
>>
>> The explanation as I understand it...
>>
>> print called on this object gets passed to print.mixfitEM(), which is:
>>
>>
>> function (x, digits = getOption("digits"), ...)
>> {
>>      family <- x$family
>>      mc <- match.call()
>>      mc$digits <- digits
>>      fun.name <- paste0("print", family)
>>      mc[[1]] <- as.name(fun.name)
>>      eval(mc, environment())
>> }
>>
>>
>> Working through the calls, when eval() is called from within funfails(),
>> mc is
>> printnormal(x = thisx, digits = 7)
>> and the calling environment does not contain thisx.
>>
>> In funworks(), it's
>> printnormal(x = x, digits = 7)
>>
>> and x is found.
>>
>> So, I can get around the problem by naming my argument x, as in
>> funworks(), but that's unsatisfying. Is there something else I can do
>> to get my functions to work?
>>
>> And what's the correct way to do what print.mixfitEM() is doing, so
>> that it works regardless? I poked around for a while, but didn't find
>> a clear (to me!) answer.
>>
>> Thanks,
>> Sarah
>>
>> --
>> Sarah Goslee (she/her)
>> http://www.numberwright.com
>>
>> ______________________________________________
>> 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.
>>
> 
> 	[[alternative HTML version deleted]]
> 
> ______________________________________________
> 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