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

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


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/bab6c026/attachment.html>


More information about the swift-evolution mailing list