Managing the arguments for nested function calls

For the sake of performance and readability, I make an effort to compartmentalize my code into functions as much as possible. But I consistently run into a problem with ballooning argument lists whenever I have nested function calls.

For example, imagine a simple series of functions like so:

functionA(A1, A2) 
functionB(B1, B2)
functionC(C1)

Individually, these are nicely compact functions with reasonably sized augment lists. But if these functions are called in a nested fashion, all the arguments must be passed down from the top level, and I end up with something like this:

functionA(A1, A2, B1, B2, C1) 
    functionB(B1, B2, C1)
        functionC(C1)

In this toy example, even the top level argument list is not unreasonable, but working on a large project I’m finding myself writing functions that require over a dozen arguments. And it’s often the case that many of those arguments are not used by the top-level function except to pass them along to other functions. This has me feeling like there must be a better way.

The obvious solution to this problem is to use global variables, so that the inner functions can access their arguments without them being explicitly passed down. But everything I read about global variables discourages their use.

So what other approaches are available to manage this issue?
Thanks in advance for your help.

2 Likes

One solution: define a struct with a dozen fields, and pass the struct to a function. Now only one argument is left.

2 Likes

Do you really have to call functionC inside functionB inside functionA?

  • If you must and there is no reasonable way of processing C1 from B1/B2 from A1/A2, then you have to pass arguments as you are now (could splat it from a collection if it helps).
  • However, if you don’t need to (like your first example of a series), then nesting those calls is just making trouble. Even if you want one call to call functions A to C and you must provide all 5 inputs at once, you shouldn’t refactor them as nested functions, just a 4th function calling them in series.
  • Another possibility is that functionA needs the result of functionB and functionC, but that doesn’t mean you have to nest their calls, you can just pass the results functionA(A1, A2, functionB(B1, B2, functionC(C1)) ), cutting down on the arguments. That’s equivalent to the series:
resultC = functionC(C1)
resultB = functionB(B1, B2, resultC)
resultA = functionA(A1, A2, resultB)

Not really a solution, you have to reassign or mutate those globals to do anything different, that’s actually more writing than longer calls. If you just want implicit default values, then use default values or callable objects.

2 Likes

I like your idea of inverting the order in which I make the calls, but I’ll need to stare at my project for a while to see if it’s feasible. Often it’s the case that the outer function exists to iteratively call the inner function and process the result. In these cases, I think your idea will work.