[swift-evolution] Guaranteed closure execution

Gwendal Roué gwendal.roue at gmail.com
Mon Feb 1 02:13:13 CST 2016


> Le 1 févr. 2016 à 06:12, Félix Cloutier via swift-evolution <swift-evolution at swift.org> a écrit :
> 
> 
>> Le 31 janv. 2016 à 23:41:59, Chris Lattner <clattner at apple.com> a écrit :
>>> Rationale for some points:
>>> 
>>>> Only one closure parameter can be marked as @noescape(once) in a function signature.
>>> 
>>> 
>>> The attribute doesn't specify the order of execution of the closures, so it could have unintended consequences if closure B depends on closure A but closure B is called first. Given the typical use case (the @noescape(once) closure as a trailing closure), I don't think that it's worth it to invest a lot of effort into coming up with a model to decide (and enforce) which closure has to be called first and what to do if either closure throws or something.
>> 
>> I don’t see a reason to have this limitation.  Definitive initialization has to be able to generate conditional code for partially initialized cases anyway, e.g.:
>> 
>> var c : C
>> 
>> if … {
>>   c = C()
>> }
>> c = C()  // could be an initialization or an assignment.
>> use(c)
>> 
>> The caller side would just have to conservatively prove that the closure bodies initialized any values they touch before using them (or that they were initialized already at the call site).
> 
> I'm not sure I communicated the concern clearly. Here's an example:
> 
>> func withNoEscape(@noescape(once) a: () -> (), @noescape(once) b: () -> ()) { /* snip */ }
>> 
>> func foo() {
>> 	let a: Int
>> 	let b: Int
>> 	withNoEscape({ a = 4 }, { b = a + 2})
>> }
> 
> 
> How does the compiler know that `a` has been assigned a value by the time that `b = a + 2` is executed? Nothing says that `withNoEscape` executes the two closures in "visual order ».

Indeed if the second closure could be made ready for both assignment or initialization of `b`, as Chris says. But here the concern is different: can even `a + 2` be computed?

Could a solution be to allow several @noescape(once) closures, but to forbid such dependency between them?
	
	let a: Int
	let b: Int
	withNoEscape({ a = 4 }, { b = 2 }) // OK
	withNoEscape({ a = 4 }, { b = a }) // Compiler error

Let me try to find reasons why we may want to support several @noescape(once) closures. Let’s imagine the context behind a use case for this.

1. Maybe one closure depends on the execution of the other. But then closures would likely take or return values, and our `a` and `b` initializations above are more like unnecessary side effects, some kind of clever trick that we may not need to support with so much convenience.

2. The two closures are internally run with code between them, code that involves a stateful API in which the ordering of statements is very important. Think OpenGL. Think our beloved UIKit. Now this may deserve attention. Because some kind of external constraint imposes the exposure of two distinct closures. It’s not fancy because it wants, it’s fancy because it must.

What do you think?

Gwendal



More information about the swift-evolution mailing list