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 <matt...@anandabits.com> wrote: > > > Sent from my iPad > > On Jun 5, 2016, at 6:56 AM, Andrew Bennett <cac...@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@swift.org> wrote: > >> >> >> Sent from my iPad >> >> On Jun 5, 2016, at 5:02 AM, Patrick Pijnappel via swift-evolution < >> swift-evolution@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@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@swift.org >>> https://lists.swift.org/mailman/listinfo/swift-evolution >>> >> >> _______________________________________________ >> swift-evolution mailing list >> swift-evolution@swift.org >> https://lists.swift.org/mailman/listinfo/swift-evolution >> >> >> _______________________________________________ >> swift-evolution mailing list >> swift-evolution@swift.org >> https://lists.swift.org/mailman/listinfo/swift-evolution >> >> >
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution