SE-0374: Add `sleep(for:)` to Clock

+1! This will make it much simpler to use clocks as generic dependencies.

3 Likes

Yes, we need this. No objections (and in fact enthusiastic support) for the proposal as written.

9 Likes

+1 Clearly a missing needed API

1 Like

LGTM, no concerns AFAICS :slight_smile:

Looks good to me; great quality of use improvement!

1 Like

Enthusiastic support for this proposal, the use cases motivating the proposal are relevant to my team and the features we implement, preview and test.

During the pitch, this comment was made:

Perhaps what follows is reading a bit too much into the above comment. While I can't imagine how else it might work, could there be a way of avoiding the existential (that is, use a concrete type only) whose behaviour can be controlled environmentally, much like how a Task can inherit priority, could it inherit a clock or the clock inherit a way to control the passage of time?

  • Can the proposed method "back-deploy", by using @available(SwiftStdlib 5.7, *) and @_alwaysEmitIntoClient attributes?

  • Should the proposed tolerance: Duration? = nil parameter also be added to the existing method on Task?

4 Likes

Availability details are at the discretion of the platform vending the stable ABI, rather than Evolution. Technically there is no obstacle to doing so in this case, and it is in-line with API that we have done this for in the past, so I expect that we would do it here as well.

I'd like @Philippe_Hausler to weigh in on this.

3 Likes

I think that is a very reasonable request that we should do; basically pull it back to as if it were introduced in the same release as Clock itself.

Per the tolerance part; that is a good catch - it is of course still available in the Instant form of that API but it would make the on-ramp of the progressive disclosure even smoother (with little to no impact on maintenance). sleep(for:) progresses to sleep(for:tolerance:) to sleep(until:clock:) to sleep(until:tolerance:clock:). So yea that seems like a very minor and reasonable addendum.

9 Likes

The question as to whether a proposed feature will be back-deployed is at the discretion of the platform vendor, but the question whether a feature can be back-deployed is a relevant question for the purposes of the Evolution process, IIUC, that falls under the “impact on ABI” part of the proposal.

5 Likes

Then, "yes, this can be back deployed using said mechanism."

7 Likes

+1, I think this is a great addition.

Many time-related APIs in Foundation use this pattern you defined as "instant-based" as their primary mechanism and I always thought it was weird that they didn't include conveniences like the proposed one given that this is what most people tend to use them for.

1 Like

+1, the texting use case is compelling.

1 Like

+1 this fits right in as it should have been part of the initial API.

1 Like

Existentials aren't so bad - especially now that we can use the language's actual existentials for PATs. I've found that the compiler is quite good at devirtualising them, so they can perform identically to generics in many situations.

For something like this where you're going to be suspending for some appreciable amount of time anyway, I think the overhead of using an existential is going to be negligible. And if you do find it to be significant, you can still opt for a restricted set of concrete types by defining an enum. In any case, this function would still be a convenient addition.

1 Like

Please don’t get me wrong, I understand and completely agree with this, but I tend to think of the method as more of a necessary addition given the obvious pain points it solves. My point in bringing it up was to stimulate some imaginative discussion where developers could control the passage of time or not with the same concrete type.

+1, great examples in the proposal!

+1 and thanks for the discussion and clarifications from the team.

Hi all --

The proposal has been updated to incorporate feedback from @benrimmington during review. I am extending the review period by an additional week to allow for comments on the additional API. It will run through Tuesday, November 1. Thank you for participating in Swift Evolution.

9 Likes

Can the Task.sleep(until:) method have a default clock, via the SE-0347 feature?

 extension Task where Success == Never, Failure == Never {
 
   public static func sleep<C: Clock>(
     until deadline: C.Instant,
     tolerance: C.Instant.Duration? = nil,
-    clock: C
+    clock: C = .continuous
   ) async throws {
     try await clock.sleep(until: deadline, tolerance: tolerance)
   }
 }

Can the Task.sleep(for:) method become generic, also with a default clock?

 extension Task where Success == Never, Failure == Never {
 
-  public static func sleep(
-    for duration: Duration
+  public static func sleep<C: Clock>(
+    for duration: C.Instant.Duration,
+    tolerance: C.Instant.Duration? = nil,
+    clock: C = .continuous
   ) async throws {
-    try await sleep(until: .now + duration, clock: .continuous)
+    try await clock.sleep(for: duration, tolerance: tolerance)
   }
 }
3 Likes