[swift-evolution] [Pitch] Improving capturing semantics of local functions

Brent Royal-Gordon brent at architechies.com
Sun Nov 12 22:11:29 CST 2017


> On Nov 12, 2017, at 12:55 AM, David Hart via swift-evolution <swift-evolution at swift.org> wrote:
> 
> Hello evolution folks,
> 
> After the positive feedback on the idea of improving capturing semantics of local functions, Alex Lynch and I worked on a proposal. Please let us know if you have any feedback:
> 
> https://github.com/hartbit/swift-evolution/blob/improving-capturing-semantics-of-local-functions/proposals/XXXX-improve-capture-semantics-of-local-functions.md

So, quoting the proposal:

> First of all, this proposal suggests extending the requirement of the self. prefix to local functions, but only if the local function is used as or used inside an escaping closure.

I don't love that the use of a function many lines away can cause errors in that closure. There's a "spooky action-at-a-distance" quality to this behavior that I don't like.

The best idea I have is to require local functions to be annotated with `@escaping` if they're to be used in an escaping closure:

    func foo() {
        // `local1` is nonescaping since it isn't marked with the @escaping attribute.
        func local1() {
            bar()
        }
        local1()		// OK, direct call
        { local1() }()	// OK, closure is nonescaping
        DispatchQueue.main.async(execute: local1)	// error: passing non-escaping function 'local2' to function expecting an @escaping closure
        DispatchQueue.main.async { local1() }		// error: closure use of non-escaping function 'local2' may allow it to escape

        @escaping func local2() {
            bar()		// error: call to method 'bar' in escaping local function requires explicit 'self.' to make capture semantics explicit
        }

        @escaping func local3() {
           self. bar()	// OK, explicit `self`
        }
        DispatchQueue.main.async(execute: local3)	// OK, escaping function
        DispatchQueue.main.async { local3() }		// OK, escaping closure
   }

    func bar() {
        print("bar")
    }

But this would be quite source-breaking. (Maybe it could be introduced as a warning first?)

> Secondly, this proposal suggests allowing the same capture list syntax from closures in local functions. Capture lists would still be invalid in top-level and member functions.


I think this is a good idea, but I don't like bringing the already weird use of `in` to actual functions.

By analogy with the current closure syntax, the capture list ought to go somewhere before the parameter list, in one of these slots:

1.	func fn<T>[foo, bar](param: T) throws -> T where T: Equatable { … }
2.	func fn[foo, bar]<T>(param: T) throws -> T where T: Equatable { … }
3.	func [foo, bar] fn<T>(param: T) throws -> T where T: Equatable { … }
4.	[foo, bar] func fn<T>(param: T) throws -> T where T: Equatable { … }

Of these options, I actually think #4 reads best; 1 and 2 are very cluttered, and 3 just seems weird. But it seems like the one that would be easiest to misparse.

-- 
Brent Royal-Gordon
Architechies



More information about the swift-evolution mailing list