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

Karl Wagner razielim at gmail.com
Tue Nov 21 17:07:27 CST 2017



> On 13. Nov 2017, at 05:11, Brent Royal-Gordon via swift-evolution <swift-evolution at swift.org> wrote:
> 
>> 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.
> 

I could go either way on that. Especially since if you want to find out where the escaping references are, you could just remove the @escaping annotation (below) and look for the compiler errors.

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

+1

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

We should keep capture lists where they are (in the body). Currently functions and closures have very similar syntax:

func doSomething(section: Int, value: Double) -> String { … }

var doSomething: (_ section: Int, _ value: Double) -> String { [captures…] s,v in }

So you basically need to replace “func” with “var”, copy your parameter names in to the body and replace them with underscores (because closure types can’t have argument labels — I think the core team do want to add those add some point IIRC… ). I don’t see a good reason to force developers to also shuffle their capture lists around. When you’re working with local functions, it’s hard enough to style your code so that you keep within your column-limit. It can be delicate, is what I’m saying.

- Karl

> -- 
> Brent Royal-Gordon
> Architechies
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution



More information about the swift-evolution mailing list