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

Charles Srstka cocoadev at charlessoft.com
Mon Jun 6 15:30:39 CDT 2016


On Jun 6, 2016, at 2:49 PM, Michael Peternell <michael.peternell at gmx.at> wrote:
> 
> That's really hard to answer in the general case. I think real proposals should contain concrete, realistic examples that show the benefit of the proposal. It's really hard to argue against a proposal if there is no such example. User feedback from a sheet is one of the few examples where asynchronous programming makes sense: But I cannot see how a `@required` annotation would be useful in that setting.

Quick-n-dirty example, written in Mail. How would you make this synchronous?

import Foundation

enum Result<T> {
	case success(T)
	case error(ErrorType)
}

enum MyError: ErrorType {
	case unknownError
	case corruptData
	case badStatusCode(Int)
}

struct SomeThing {
	init?(data: NSData) {
		...
	}
}

func getSomethingFromTheNetwork(url: NSURL, completionHandler: (Result<SomeThing>) -> ()) {	
	let task = NSURLSession.sharedSession().dataTaskWithURL(url) { data, response, error in
		if let error = error {
			completionHandler(.error(error))
			return
		}

		if let httpResponse = response as? NSHTTPURLResponse {
			let statusCode = httpResponse.statusCode
			
			if statusCode < 200 || statusCode >= 300 {
				completionHandler(.error(MyError.badStatusCode(statusCode)))
				return
			}
		}
	
		guard let data = data else {
			completionHandler(.error(MyError.unknownError))
			return
		}

		guard let something = SomeThing(data: data) else {
			completionHandler(.error(MyError.corruptData))
			return
		}

		completionHandler(.success(something))
	}
	
	task.resume()
}

(disclaimer: yes, we’d probably have to improve the API for NSURLSession a bit here for @required to be useful.)

How would you make this synchronous:

func getSomethingFromAnotherTask(completionHandler: (Result<SomeThing>) -> ()) {
	let message = ...

	xpc_send_message_with_reply(self.connection, message, self.dispatchQueue) { reply in
		do {
			let something = try self.turnReplyIntoSomethingSomehow(reply)
		
			completionHandler(.success(something))
		} catch {
			completionHandler(.error(error))
		}
	}
}

Or this:

func doSomethingThatNeedsUserInput(completionHandler: (Bool) -> ()) {
	let alert = NSAlert()

	alert.messageText = “Should we continue?”
	alert.addButtonWithTitle(“Continue”)
	alert.addButtonWithTitle(“Cancel”)

	alert.beginSheetModalForWindow(someWindow) { response in
		if response == NSAlertFirstButtonReturn {
			completionHandler(true)
		}

		// uh oh, I forgot to test for other conditions, and now the completion handler won’t be called if the user clicked “Cancel”.
		// Too bad the compiler couldn’t warn me about it.
	}
}

There are some tasks which synchronous programming is simply not well-suited for.

Charles

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160606/ab4fa220/attachment.html>


More information about the swift-evolution mailing list