[swift-evolution] [Idea][Swift 3] Change 'DispatchQueue.after' to take a relative time + clock (with default). Maybe replace clocks with an enum?

Pierre Habouzit phabouzit at apple.com
Wed Jul 13 11:56:19 CDT 2016

> On Jul 13, 2016, at 9:18 AM, Karl via swift-evolution <swift-evolution at swift.org> wrote:
> #1 - 
> Currently, DispatchQueue.after() takes an absolute time as the “after” parameter. This makes it hard to understand how to use it; you need to go digging through generated interfaces to find out what a ‘DispatchTime’ is and how you construct it (by getting the .now() and adding a DispatchTimeInterval using one of the easily-missable operator overloads, if you were wondering).
> Here is what DispatchQueue.after looks like now:
> public func after(when: DispatchTime, qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], execute work: @convention(block) () -> Void)
> So to use it, you have to type:
> DispatchQueue.main.after(when: DispatchTime.now() + .milliseconds(250))   { /* do stuff */ }
> I don’t believe this is a great fit with the Swift API Guidelines. I believe the name “after” already implies that the time is relative, the argument label should be dropped, and that nuances about the clock (basically, whether or not it pauses during system sleep) are edge-cases which can be handled for the majority of users with a sensible default value (or better yet, modelling via an enum or boolean switch — see #2). Additionally, There are overloads with “wallTime” parameter labels which seem only to duplicate type information (the monotonic and walltime clocks are actually split at the type level) and could be more concise and readable. The principle is that, ultimately, you should just be able to write the above code like this:
> DispatchQueue.main.after(.milliseconds(250)) { /* do stuff */ }
> Or
> DispatchQueue.main.at(DispatchTime.now() + .seconds(3)) { /* do stuff */ }
> It’s not something you use all the time (like .sync/.async), but I find that whenever I do need it I’m frustrated by how needlessly complex it is to decipher and use. I would find these methods much more obvious, I could figure out how to use them much more quickly, and I think I’d remember how to use them more quickly.

As mentioned by Matt in the pull request you created, we’re working on a proposal that would look like this:

func asyncAfter(deadline:qos:flags:work:)
func asyncAfter(wallDeadline:qos:flags:work:)
As we discussed on the pull request, 

DispatchQueue.main.after(.milliseconds(250)) { /* do stuff */ }

Is ambiguous as you don’t know which clock was meant, and despite your claim, there’s in general no good default for the clock. If you are writing a calendar app, you want wallClock, but if you’re doing anything network related, you will want the monotonic one. Depending on which software you’re writing, you will have a bias for one or the other.

Also it may be a native speaker thing (which I’m not) but this reads “please async this closure on `q` after this deadline has expired” to me, which sounds like proper english:

q.asyncAfter(deadline: .now() + 1.0) { /* do stuff */ }

I thought that you would say “please meet me in 10 minutes” or “please meet me after 2PM”. The `asyncBy` that you suggested to the pull request reads *before* that deadline to me which is not good either.
`asyncAt` would probably work too however, but asyncAfter is easier for people coming from C who are used to dispatch_after().

> #2 - 
> Actually, while writing this down it’s becoming clearer and clearer that the idea to split DispatchTime (the monotonic clock) and DispatchWallTime (the wall clock) at the type level is probably a bad idea.
> Novice users are not going to understand what’s going on here - I expect most of them to default to the more generic-sounding “DispatchTime” without any idea of the implications of this. Perhaps we should look at replacing these clock variations with a more descriptive enum or boolean, rather than a separate type. For example:
> struct DispatchTime {                       // replaces DispatchTime and DispatchWallTime
>    let rawValue : dispatch_time_t
>    let clock : Clock
>    enum Clock { case monotonicClock; case wallClock }
> }
> This would not obsolete the discussion at the start about “after”. The name “after” still implies that I want something done at some duration relative to an absolute point in time (usually now).

This is what dispatch_time_t does in C and my team at Apple widely considers this having been a design mistake: it means that the time is not generally Comparable, that you can’t perform any kind of arithmetic with it, etc.

In C it’s more convenient to have a single type, but in Swift where type is inferred, given that most of the uses will construct the time as the argument to the function itself, we feel that the current proposal allows for the most concise use.

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

More information about the swift-evolution mailing list