Very nice, thank you!
Sarah On Thu, May 25, 2023 at 8:20 PM Iris Simmons <ikwsi...@gmail.com> 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.gos...@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 >> ______________________________________________ R-help@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.