2

I have a function in python that calls itself recursively, and has some internal variables passed down the recursion using keyword arguments (that are not listed in the docstring)

Is it a problem to have this exposed to an user instead of defining another private recursive function that is called by a public function. Where the public function would be a wrapper around the recursive function.

I cannot think of any downsides of the first approach compared to the second.

2
  • 2
    If the recursive function doesn't take any parameters that wouldn't be there in the non-recursive wrapper, then it's not really a problem; but usually there are parameters that are really an implementation detail and are only there because the implementation uses recursion. In that case, having a client-facing wrapper lets you change the implementation without affecting clients (e.g., you might decide to use an iterative solution at some point, say for performance reasons). It also makes your client code more self-contain and more sensible to the reader. Commented Apr 1, 2021 at 19:41
  • Very unfortunately, all functions is Python are user callable.
    – h22
    Commented Jun 11, 2021 at 11:06

3 Answers 3

4

I would recommend the wrapper-function approach. There's two downsides to skipping it that I can think of:

Wasteful validation

Exposing the recursive variant means you have to do argument validation on every recursion, which might be wasteful, depending on the kind of validation you need.

For example, fib isn't defined for negative numbers. Ideally, you would have an "entry point" public function which can check for the negative once, and then call off to a private recursive function which can just safely assume the input is non-negative.

Leaking implementation details

A lot of recursive function have parameters that exist only for the sake of supporting the recursion. Typically, your public "entry point" function will pre-populate these with appropriate values for the start of the recursion. If you didn't have that, your parameters will be visible to the rest of the world. Default parameters can help with this, but the existence of these still parameters still leak, and it clutters your API.

Consider also that this prevents you from being able to change the implementation in the future. For example, if you had a function that recursively reverses a linked list, you would benefit from having an accumulator parameter that helps you be able to take advantage of Tail Call Optimization. If you then decided to switch to an iterative approach, you can't remove your accumulator parameter, because that would be breaking your public API.

2
  • So, your recommendation then is to "Use the Wrapper Function?" Commented Apr 2, 2021 at 19:50
  • Yep. I'll edit my question to clarify
    – Alexander
    Commented Apr 2, 2021 at 20:11
3

Providing the public wrapper to the recursive function is a better way to expose this functionality to your end-users because they only see the arguments that they need to see. Sure, you could expose the recursive function with all the arguments, and then let the function's documentation explain what values to use, or let them default to correct values, but it's not very user-friendly AND it opens up your recursive function to whatever your end-users might want to try to put into those extra arguments, causing who knows what behaviour?

1

It's not the worst thing, but in general exposing too many internal details is causing extra burden on maintainers. They see the extra variables and have to figure out if they need to worry about them or not, and if so, what do the variables do. Unfortunately, it's too common to forget to update a docstring.

Needing extra "internal" arguments is fairly common with recursive functions, and they usually need to get initialized to something specific like an empty list, so you'll often see the pattern of a "helper" function defined inside the actual user interface function in languages that support it:

def public_recursive_function(n):
    def helper(n, acc):
        pass
    helper(n, [])

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.