[swift-evolution] Guaranteed closure execution

Jacob Bandes-Storch jtbandes at gmail.com
Sat Jan 30 14:19:57 CST 2016


The "noescape + exactly once" guarantee is useful to the compiler because
it allows the closure to *initialize* variables that it captures. The way
to think about this is that the closure body is effectively inlined.

"noescape, but not exactly once" is still useful because you can omit
references to self, and know that variable modifications in the closure
will be visible after the function call. But you can't perform
initialization (because the closure might be called more than once), nor
can you be sure variables are initialized after the function call (because
the closure might not be called at all).

"exactly once, but escaping" is an important API contract that should be
documented, but if the closure can escape the call, then you still can't
perform initialization in the closure, because the order it happens in
w.r.t. the function call isn't guaranteed. I might be missing something,
but I don't see any reason for this feature to exist, because...well, it
isn't really a feature if it can't do anything.

Jacob

On Sat, Jan 30, 2016 at 12:07 PM, Matthew Johnson via swift-evolution <
swift-evolution at swift.org> wrote:

>
> On Jan 30, 2016, at 1:56 PM, Félix Cloutier via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> I agree that many closures are expected to be executed only once, but it
> seems to me that it's only useful to the compiler if it's also marked
> @noescape. A completion handler marked "once" without "noescape" doesn't
> allow the compiler to determine that a variable will have been initialized
> after the call that takes the closure.
>
> So in these other cases, it only serves as documentation. I'm not very
> enthusiastic about attributes that only serve as documentation because
> there is an infinity of attributes that can be added to the language for
> documentation purposes.
>
>
> It also serves as a guarantee that the closure is executed exactly once.
> Even if the compiler can’t use it for optimization the guarantee could be
> useful.
>
>
> There are other places than `dispatch_sync` where you may want a
> `@noescape(once)` attribute. Any function that executes a closure while
> providing some sort of guarantee/RAII-like handle can benefit from it. Most
> of the functions starting with `with` in the standard library could benefit
> from it: withExtendedLifetime, withUnsafePointer and friends, withVaList.
> String's `withCString` and `withMutableCharacters` could benefit from it
> too. Someone who writes a `withLock(lock) { closure }` function would be
> happy to have it too.
>
> Félix
>
> Le 30 janv. 2016 à 07:11:23, Rod Brown <rodney.brown6 at icloud.com> a écrit
> :
>
> I'm very supportive of this type of proposal.
>
> I do agree with Brent though that @noescape and 'once' is somewhat
> orthogonal.
>
> There are a lot of places where you want to be clear that the block will
> be called but no more than once. For example, the NSURLSession callback
> blocks you would expect never to be called multiple times. Completion
> handlers are almost always used only once. I don't think this case, which
> is extremely common, can be overlooked.
>
> On the other hand, I suspect the majority of places you use closures with
> @noescape, it seems more likely you'd *want *it used multiple times, like
> a search, filter, find etc of multiple items in a collection or group,
> otherwise you'd generally just put the closured activity before or after.
> The only areas where I would expect to see such closures of @noescape and
> 'once' would be dispatch_sync, or somewhere where you want to invoke custom
> code in the middle of a complex operation of a method.
>
> It seems to me that separating them, but allowing them to be used
> together, makes more sense.
>
> Rod
>
>
> On 30 Jan 2016, at 5:05 AM, Félix Cloutier via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> I don't have much to add but I also think that it would be nice to have.
>
> Félix
>
> Le 29 janv. 2016 à 12:38:01, Chris Lattner via swift-evolution <
> swift-evolution at swift.org> a écrit :
>
> On Jan 29, 2016, at 12:23 AM, Jacob Bandes-Storch via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> I've wanted something like this as well. I think it would be harder than
> it seems, because "x = 1" might need to perform initialization, or
> assignment, depending how it's used.
>
> It could make sense to have something like
> "@noescape(executed_exactly_once)" but this might be so limited it's not
> worth it. And I'm not sure how it should interact with throws.
>
>
> I think that something like this is implementable, and making it a
> modifier to @noescape is sensible.
>
> The semantics we could support is that the function is guaranteed to call
> the closure exactly once on any path that could lead to a return or throw.
>
> This approach allows you to pass the closure down the stack, and composes
> with error handling.  It is obviously limited what you can do with the
> closure, but that is necessary to validate correctness.
>
> -Chris
>
>
>
>
> Jacob
>
> On Thu, Jan 28, 2016 at 11:38 PM, Gwendal Roué <swift-evolution at swift.org>
>  wrote:
>
>> Hello,
>>
>> I’d like to discuss the opportunity to let functions declare that a
>> closure argument is guaranteed to have been executed when the function has
>> returned.
>>
>> For example:
>>
>> func f(@noescape(executed) closure: () -> ()) {
>>     closure()
>> }
>>
>> The expected advantage is that the compiler would know that a variable
>> set inside the closure is guaranteed to be initialized, and that it can be
>> used after the execution of the function, as below:
>>
>> let x: Int  // Not initialized
>> f { x = 1 }
>> print(x)    // Guaranteed to be initialized
>>
>> Today developers have to write pessimistic code like below:
>>
>> var x: Int = 0 // `var` declaration, with some irrelevant value
>> f { x = 1 }
>> print(x)
>>
>> As for a real world usage, I’d like to access a database in a safe
>> (queued) way, and fetch values out of it:
>>
>> let items: [Item]
>> let users: [User]
>> dbQueue.inDatabase { db in
>>     items = Item.all().fetchAll(db)
>>     users = Item.all().fetchAll(db)
>> }
>>
>> Gwendal Roué
>>
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160130/fc9fec8d/attachment.html>


More information about the swift-evolution mailing list