Best way to pass many parameters to functions

Hello, I'm developing a function that uses several other functions and each of them requires a set of arguments. I wonder what is the best practice to pass those arguments to my function?

Example: Let's say myfunction() depends on aaa_fun(), bbb_fun(), ccc_fun(), and ddd_fun(), where each xxx_fun() requires three arguments xxx_par1, xxx_par2, and xxx_par3.

Is there any reason I should avoid writing my function as

myfunction <- function(
    data,
    aaa_pars = list(aaa_par1, aaa_par2, aaa_par3),
    bbb_pars = list(bbb_par1, bbb_par2, bbb_par3),
    ccc_pars = list(ccc_par1, ccc_par2, ccc_par3),
    ddd_pars = list(ddd_par1, ddd_par2, ddd_par3)
){
    # call aaa_fun()
    res_aaa <- aaa_fun(
        data = data,
        aaa_par1 = aaa_pars$aaa_par1, 
        aaa_par2 = aaa_pars$aaa_par2, 
        aaa_par3 = aaa_pars$aaa_par3, 
    )
   # similarly for function calls to bbb_fun(), ccc_fun(), and ddd_fun()

}

The reason I put arguments related to each xxx_fun() function in a list is to make the argument list of myfunction()look cleaner, but maybe this is not a good practice for developing packages. Any suggestions?

1 Like

Do you have default values in mind for aaa_par1 etc., or does the user need to specify every parameter explicitly?

We have default values for 90% of the parameters.

A couple of possibilities come to mind. One is to define your function as myfunction <- function(data, aaa_par1 = default1, aaa_par2 = default2, <etc>) and let the user enter just non-default values paired with the parameter names.

Another is to define the function as myfunction <- function(data, ...) and tell the user that "..." means any name = value parameter entries to be passed to the component functions. Inside the code for myfunction you would initialize all parameters with default values, then scan the optional arguments to assign nondefault values, and then call the component functions. You can find a discussion of the pros and cons of this method here (near the end of the section).

Either of these methods, or your proposed method, will work. As noted in the linked document, some approaches may be more vulnerable to typos than others (meaning whether the user misspelling "aaa_par1" results in an error message or the program silently ignoring the user's entry and going with the default value). If users are likely to use default arguments for most parameters, I think the "..." argument approach results in the least work for the user.

Agreed with prubin. I would also mention the possibility to have a helper function that generates the list of options.

The main problem I can think of is discoverability: the user can't just glance at the help page to see available arguments, and you have to make sure your option lists are documented somewhere. But, especially if it's options that most users won't change, I don't really see a problem.

There are a few packages that use such an "option object", although usually only one per function; but I don't see why having several would become bad practice.

To me, this is clearly the sign of a poor design. What's your motivation for writing such a function? Is it because all those inner functions interact with the same data? How each child function relates to the others? What do each of these (childs and parent) functions return?

This topic was automatically closed 42 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.