µPitch: add `Duration.nanoseconds(_:)`

Duration has the following static methods:

public static func seconds<T: BinaryInteger>(_ seconds: T) -> Duration
public static func seconds(_ seconds: Double) -> Duration
public static func milliseconds<T: BinaryInteger>(_ milliseconds: T) -> Duration
public static func milliseconds(_ milliseconds: Double) -> Duration
public static func microseconds<T: BinaryInteger>(_ microseconds: T) -> Duration
public static func microseconds(_ microseconds: Double) -> Duration
public static func nanoseconds<T: BinaryInteger>(_ value: T) -> Duration

For no good reason, the obvious additional method:

public static func nanoseconds(_ nanoseconds: Double) -> Duration

was omitted from the proposal and implementation. It's somewhat useful. It's relatively simple to implement inside of Duration (where we can use _Int128), but relatively hard to implement well outside of the standard library. We should add it.

An implementation is available here.

30 Likes

I assume it was omitted because it’s always going to truncate? round? the value, and Duration is documented to not have sub-nanosecond precision. But it doesn’t seem harmful to have it when someone could just as easily say Duration(milliseconds: value / 1000) as Duration(nanoseconds: UInt64(value)).

2 Likes

To be clear, Duration has attosecond precision. The APIs that do stuff with durations generally do not.

6 Likes

I completely misremembered. Yeah, I can’t think of a reason either then.

Since we’re on the topic of omissions from these APIs, can we add the overloads that make + commutative (as Strideable already does, and thus all clock and duration types, but not instant types) as well as *?

extension InstantProtocol {
  // Exists:
  public static func + (_ lhs: Self, _ rhs: Duration) -> Self
  // Omitted:
  public static func + (_ lhs: Duration, _ rhs: Self) -> Self
}

public protocol DurationProtocol: Comparable, AdditiveArithmetic, Sendable {
  // Exists:
  static func * (_ lhs: Self, _ rhs: Int) -> Self
  // Omitted:
  static func * (_ lhs: Int, _ rhs: Self) -> Self
}

extension Duration {
  // Exists:
  public static func * (_ lhs: Duration, _ rhs: Double) -> Duration
  public static func * (_ lhs: Duration, _ rhs: Int) -> Duration
  // Omitted:
  public static func * (_ lhs: Double, _ rhs: Duration) -> Duration
  public static func * (_ lhs: Int, _ rhs: Duration) -> Duration
}
2 Likes

Agree on the addition and that it would be good to do a holistic pass about what else is missing here and do it all together.

1 Like

Assuming these don’t hurt typechecker performance, sure.

1 Like

Agreed, and I will, but we should be willing to take small proposals for obvious missing API without asking every would-be contributor to do this. The possibility of other improvements doesn’t have to hold up a small obvious change.

15 Likes

Missed opportunity to call this a nano-Pitch :sweat_smile:

9 Likes

Seems reasonable. Can this also be @backDeployed?

1 Like

Yes, it can be.

2 Likes

I thought about it, but a "nanopitch" feels more like one of our "change five words in a docc comment" proposals.

4 Likes

The problem with Strideable is that it would require SignedNumeric which requires being able to multiply two durations together and get the same type back (which is kinda nonsense)

Sorry, I’m not proposing Strideable conformance; I’m saying that those types in the proposal which already conform to Strideable have commutative +, but other types don’t have commutative +, and we can take the opportunity to address that inconsistency here because being able to add a duration to an instant in one order but not another is not an entirely intuitive limitation (or necessary, unless it messes up overload resolution performance).

4 Likes

What algorithms are used by typechecker or how does one know a particular change affects its performance?

We ask the CI system to measure compiler performance on a PR. This isn't perfect, but it's the best proxy we have.