Private Function Parameters?

Hi, all

I'd like to start a new SE proposal regarding private function parameters, and I have searched the site and no similar proposals/posts are present. The detail is as follows:

I'd like to propose the support for private function parameters, which is a parameter non-visible to the outside of the class/struct/whatever. The useful cases includes, but is not limited to, when you want to preserve states between recursive calls and do not want to create an instance variable for that. Example would be when a loadData function needs to retry once and only once when it fails, so it uses a parameter retryIfFailed to determine whether it should retry.

class DataManager {
    // parameter retryIfNeeded is private
    func loadData(id: String, retryIfNeeded: Bool = true, completion: @escaping (Data) -> Void) {
        // load data...
        if (failed) {
            if (retryIfNeeded) {
                loadData(id: id, retryIfNeeded: false, completion: completion)
            } else {
                // report failure
            }
        }
    }
}

In the case above, retryIfNeeded should both have a default value and be inaccessible from outside of DataManager, as there should be no way to configure it.

Proposed new function signature:
func loadData(id: String, retryIfNeeded: private Bool = true, completion: @escaping (Data) -> Void)

One could argue that this can be achieved via making this method entirely private and creating a public method without the parameter. This is true, and is exactly what I intend to have the compiler do for me, should Swift supports this feature. Specifically, the compiler already has similar mechanisms regarding default parameters. When the compiler finds functions with default parameters, it automatically generates "specialized" versions of the same function and calls one of those at callsites if parameters are left out. If the compiler can do the same — generate specialized versions of methods with private parameters left out and calls those at callsites outside of the class — it would save us programmers lots of time, just like how default parameters has already save us lots of time.

Given the context, supporting private parameters should not be hard to implement, and any argument that goes against it because it's just like a "syntactical sugar," would mostly likely also go against default parameters since it's also just kind of a "syntactical sugar" that's automatically converted into multiple specialized functions by the compiler.

Please share your opinion on this!

Thanks,
Nick

2 Likes

It's good, but add internal and fileprivate as well.

1 Like

At first glance, I thought this idea wouldn't be much of a win, but on reflection I think maybe it's useful.

The only thing I would suggest would be to use some kind of visibility attribute (e.g. @visibility(private) etc) rather than naked keywords. As pitched, I think the keywords could (theoretically) be source breaking.

That's not actually how default arguments work. Default arguments work by implicitly adding the default argument at the call site. That is, when the compiler sees this code:

func x(y: Int = 1) { ... }
x()

You're imagining it does this:

func x(y: Int) { ... }
func x() { x(y: 1) }
x()

But it actually does (something more like) this:

func x(y: Int) { ... }
x(y: 1)

(That's why you can use any combination of default arguments at a particular call site, and it's why a library update that changes a default argument doesn't affect your code until you recompile it.)

This approach would be more difficult for private arguments. Presumably you'd want them to be able to use private (or otherwise less-visible) types, but those wouldn't necessarily be accessible at the call sites that needed to use them. It would be particularly difficult to make this work with library evolution—adding parameters is ABI-breaking even if they have default arguments, so you wouldn't be able to use this feature to add or remove nonpublic arguments.

Overall, I think it's better to leave things be. When you account for all the caveats and limitations, I don't think this is a big enough improvement over just writing a private method with extra parameters.

11 Likes

What if I define functions like:

func publicFunc(privateParam: private P, publicParam: A) {
    // ...
}
func publicFunc(publicParam: A, privateParam: private P) {
    // ...
}