[swift-evolution] Pitch: @required attribute for closures
Andrew Bennett
cacoyi at gmail.com
Sun Jun 5 08:52:21 CDT 2016
Storing into a member would be fine, as long as it must keep @once as a
type annotation and the compiler makes sure you maintain:
sum(callCount, storeCount, passCount) == 1
For example:
class Example {
private var closure: (@once (T) -> Void)?
func callClosure(value: T, replace: (@once (T) -> Void)? = nil) {
// the compiler should error if it detects the closure:
// * escaping more than once, while still being stored,
// * or being called while still being stored or escaping,
// * or being overwritten without being called
if let closure = self.closure {
self.closure = replace
closure(value)
}
}
deinit {
// compiler warning: that closure is potentially un-called
// runtime fatalError if it's .Some(Closure) after deinit
}
}
There could be a standard library type with those guarantees built in.
On Sun, Jun 5, 2016 at 10:12 PM, Matthew Johnson <matthew at anandabits.com>
wrote:
>
>
> Sent from my iPad
>
> On Jun 5, 2016, at 6:56 AM, Andrew Bennett <cacoyi at gmail.com> wrote:
>
> I like this.
>
> One of the suggestions on @noescape(once) was that it just becomes @once
> and works with escaping closures too. It might be possible if compile time
> checks verified that the closure isn't copied, and that it is called before
> being deinit-ialized. Failing that I'm happy with a runtime circumstance in
> the cases the compiler can't check.
>
>
> Yeah, maybe if it is only used asynchronously and never stored in a member
> or global it could be verified and that is a pretty common case. That
> would certainly be easier than the general case.
>
> I prefer @once over @required if the guarantee is single execution. If
> the guarantee is *at least once* obviously @once is not the right
> attribute, but I'm not convinced @required is either. Maybe @invoked.
>
>
> It would be great if @required took into the account the feedback from
> that proposal and considered the synchronous case too.
>
> As an aside, you can get some of the guarantees you want like this:
>
> func doSomething(completionHandler: (SomeEnum) -> ()) {
>
> dispatch_async(someQueue) {
>
> let result: SomeEnum
>
> // the compiler ensures 'result' is set
>
> defer { completionHandler(result) }
>
>
> if aCondition {
>
> if bCondition {
>
> result = .Foo
>
> } else {
>
> result = .Bar
>
> }
>
> // the compiler ensures you do this, because it is 'let'
>
> return
> }
>
>
> if cCondition {
>
> result = .Baz
>
> }
>
> }
>
> }
>
> On Sun, Jun 5, 2016 at 9:42 PM, Matthew Johnson via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>>
>>
>> Sent from my iPad
>>
>> On Jun 5, 2016, at 5:02 AM, Patrick Pijnappel via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>> This has actually been proposed before, see SE-0073:
>> https://github.com/apple/swift-evolution/blob/master/proposals/0073-noescape-once.md
>>
>>
>> Actually that proposal was for noescape closures and this suggestion is
>> for escaping closures. I don't think the compiler can verify this for
>> noescape closures. If it is possible it would be far more complicated.
>>
>>
>>
>> On Sun, Jun 5, 2016 at 11:37 AM, Charles Srstka via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>>> MOTIVATION:
>>>
>>> As per the current situation, there is a pitfall when writing
>>> asynchronous APIs that does not occur when writing synchronous APIs.
>>> Consider the following synchronous API:
>>>
>>> func doSomething() -> SomeEnum {
>>> if aCondition {
>>> if bCondition {
>>> return .Foo
>>> } else {
>>> return .Bar
>>> }
>>> } else {
>>> if cCondition {
>>> return .Baz
>>> }
>>> }
>>> }
>>>
>>> The compiler will give an error here, since if both aCondition and
>>> cCondition are false, the function will not return anything.
>>>
>>> However, consider the equivalent async API:
>>>
>>> func doSomething(completionHandler: (SomeEnum) -> ()) {
>>> dispatch_async(someQueue) {
>>> if aCondition {
>>> if bCondition {
>>> completionHandler(.Foo)
>>> } else {
>>> completionHandler(.Bar)
>>> }
>>> } else {
>>> if cCondition {
>>> completionHandler(.Baz)
>>> }
>>> }
>>> }
>>> }
>>>
>>> Whoops, now the function can return without ever firing its completion
>>> handler, and the problem might not be discovered until runtime (and,
>>> depending on the complexity of the function, may be hard to find).
>>>
>>> PROPOSED SOLUTION:
>>>
>>> Add a @required attribute that can be applied to closure arguments. This
>>> attribute simply states that the given closure will always be eventually
>>> called, and the compiler can enforce this.
>>>
>>> DETAILED DESIGN:
>>>
>>> - The @required attribute states in our API contract that a given
>>> closure *must* be called at some point after the function is called.
>>>
>>> - Standard API calls like dispatch_async that contractually promise to
>>> execute a closure or block get @required added to their signatures.
>>>
>>> - When the compiler sees a @required closure in a function declaration,
>>> it checks to make sure that every execution path either calls the closure
>>> at some point, or sends a @required closure to another API that eventually
>>> ends up calling the closure.
>>>
>>> - If there’s a way for a @required closure not to be called, the
>>> compiler emits an error letting the developer know about the bug in his/her
>>> code.
>>>
>>> IMPACT ON EXISTING CODE:
>>>
>>> None. This is purely additive.
>>>
>>> ALTERNATIVES CONSIDERED:
>>>
>>> I got nothin’.
>>>
>>> Charles
>>>
>>> _______________________________________________
>>> 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/20160605/39de521d/attachment.html>
More information about the swift-evolution
mailing list