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
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?
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.
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?