4

I'd like to write a more-or-less generic caller to targetf that retains its default parameters.

Suppose we have a provided by some 3rd party library targetf:

targetf<-function(x=1,...){
    print(paste("x =",x))
}

How to write wrapperf, that will respect targetfs default arguments, so calling wrapperf() would not yield the error message Error in paste("x =", x) : argument "x" is missing, with no default?

The obvious candidate wrapperf1<-function(x,y) {targetf(x=x)} doesn't work and fails with wrapperf1().

The wrapperf2<-function(...) {targetf(...)} behaves correctly, but it doesn't work for me, because I only care to pass the x argument, (and possibly reserve the ... to other functions in wrapperf body).

Maybe to solve the issue I'd have to play with ellipsis filtering, which is a terra incognita for me at the moment...


One idea on how to solve the problem: maybe I'd need to create a specially crafted ... object from scratch in wrapperf to do pseudo code like this:

wrapperfX<-function(x,y,...)
{
    ...<-if(missing(x){
            list()
        }else{
            list(x=x)
        }
    targetf(...)
}

But I have no idea how to even start doing assignments into ellipsis... are the possible at all? I put this question separately on SO: Is it possible to create an ellipsis () object from scratch


Since the problem is still unsolved I decided to post this question to r-help@r-project.org

Community
  • 1
  • 1
Adam Ryczkowski
  • 7,592
  • 13
  • 42
  • 68
  • You could use "list(...)" and check in result if you find your argument. Anyhow, there is a problem if you declare wrapperf1 with two arguments (without default values), and call it with none. See also [here](http://stackoverflow.com/questions/3057341/how-to-use-rs-ellipsis-feature-when-writing-your-own-function). –  Sep 07 '13 at 07:37
  • 1
    +1. Interesting question. This seems relevant: http://stackoverflow.com/questions/7964830/test-if-an-argument-of-a-function-is-set-or-not-in-r – Frank Sep 07 '13 at 07:42
  • 1
    @Frank Of course I can manually cook calls to `targetf` with all possible combinations of missing parameters (which number will explore like n!) but it wouldn't be much general. – Adam Ryczkowski Sep 07 '13 at 07:50
  • But it gave me idea: since R is interpreted, I can create such wrapper function dynamically... that would be a little cheating, but in principle it should work. And the generating function will definitely not be one-liner... – Adam Ryczkowski Sep 07 '13 at 07:51
  • Why not put the same default parameters in the argument list of your wrapper function? – Carl Witthoft Sep 07 '13 at 15:10
  • @Carl Because I never know, which parameters have default values. Passing missing parameters for which the default exists, will override the default with missing, which is what I want to avoid. – Adam Ryczkowski Sep 08 '13 at 16:04
  • Then you're pretty much limited to using `list(...)` internally. But unless you're trying to write a wrapper which passes the "wrapped" function as one of its parameters, you do in fact always know what the defaults are for the function you're calling, so I still don't see the problem. – Carl Witthoft Sep 08 '13 at 18:48
  • @Carl Then how? `formals(targetf)` doesn't help here, if the `targetf` calls another function with default parameters, using `nestedtargetf(...)`; e.g. the `fitdist` function from `fitdistplus` library calls internaly `mmedist(...)`. – Adam Ryczkowski Sep 09 '13 at 08:02
  • @SimonO101 Of course. I didn't know they exist. I could swear, that've I never seen a flag, that someone did asnwer them. – Adam Ryczkowski Sep 13 '13 at 12:11
  • I've also got a reply from Gerrit Eichner from r-help@r-project.org list, that suggests to use `match.call` with `expand.dots=FALSE`. It's interesting thought, and I'll try to check it out. – Adam Ryczkowski Sep 13 '13 at 12:34

1 Answers1

1

I'll reply myself with even more elaborate setup (I replaced targetf with lib* names). The bar functions are nested inside foo ones, so their default values are not easily visible to the user.

libfoo<-function(par1=1,...)
{
    if (missing(par1))
    {
        warning("par1 is missing!!")
    }
    print(paste0('par1: ',par1))
    libbar(...)
}


libbar<-function(par2=1)
{
    if (missing(par2))
    {
        warning("par2 is missing!!")
    }
    print(paste0('par2: ',par2))
}


libfoo2<-function(par3=1,...)
{
    if (missing(par3))
    {
        warning("par3 is missing!!")
    }
    print(paste0('par3: ',par3))
    libbar2(...)
}

libbar2<-function(par4=1)
{
    if (missing(par4))
    {
        warning("par4 is missing!!")
    }
    print(paste0('par4: ',par4))
}

The wrapper:

wrapfun<-function(x,par1=3,...,par3,par4)
{
    libfoo(par1,...) #With dots everything is simple

    pars<-list()
    if(!missing(par3))
    {
        pars<-c(pars,par3=par3)
    }
    if(!missing(par4))
    {
        pars<-c(pars,par4=par4)
    }
    do.call(libfoo2,pars) #This is how we can pass specific arguments, and respecting missings properly.
}

This is some output:

wrapfun(par1=5,par2=5,par3=5,par4=5)
# [1] "par1: 5"
# [1] "par2: 5"
# [1] "par3: 5"
# [1] "par4: 5"


wrapfun()
# [1] "par1: 3"
# [1] "par2: 1"
# [1] "par3: 1"
# [1] "par4: 1"
# Warning messages:
# 1: In libbar(...) : par2 is missing!!
# 2: In (function (par3 = 1, ...)  : par3 is missing!!
# 3: In libbar2(...) : par4 is missing!!

wrapfun(par4=5)
# [1] "par1: 3"
# [1] "par2: 1"
# [1] "par3: 1"
# [1] "par4: 5"
# Warning messages:
# 1: In libbar(...) : par2 is missing!!
# 2: In (function (par3 = 1, ...)  : par3 is missing!!
Adam Ryczkowski
  • 7,592
  • 13
  • 42
  • 68