[swift-evolution] Pitch: @required attribute for closures

Andrew Bennett cacoyi at gmail.com
Sun Jun 5 06:57:56 CDT 2016


"runtime circumstance" -> "runtime assertion", weird typo.

On Sun, Jun 5, 2016 at 9:56 PM, 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.
>
> 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/00ef7d6d/attachment.html>


More information about the swift-evolution mailing list