so ClockMarks aren't Comparable, which makes sense...
# stdlib
j
so ClockMarks aren't Comparable, which makes sense since you cannot really meaningfully compare them across Clocks, but also it's a bit annoying since frequently you're in a single Clock "namespace" where it would be nice to not have to continually unwrap them
i
What do you mean by unwrapping?
j
calling
elapsed()
For example:
Copy code
var expiration = expirationClockMark
val mark = clock.mark()
if (mark.elapsed() >= expiration.elapsed()) {
where it feels like it should be
Copy code
var expiration = expirationClockMark
val mark = clock.mark()
if (mark >= expiration) {
There's no type system tricks (as far as I know) to restrict this at compile time to two instances which originated from the same
Clock
. But maybe a runtime check is enough? Things like
java.nio.path.Path
come to mind where it's tied to a
FileSystem
and you cannot meaningfully do operations on two `Path`s from different `FileSystem`s. Despite this, there are methods which work on two instances (like
Path.resolve(Path)
) which just runtime-check the invariant. The only problem I see is that it would become the responsibility of any custom `Clock`/`ClockMark` implementation to ensure they implement this behavior.
i
if (mark.elapsed() >= expiration.elapsed())
If these two marks are from the same clock, this expression would be almost always true or always false, because
elapsed
values of both of them increment simultaneously. What are you trying to achieve by this comparison?
j
er, i assumed a mark was an immutable snapshot of the current elapsed time (equivalent to calling something like
System.nanoTime()
)
i
The mark itself stands still, it's just the time goes forward 🙂.
You can implement expiration as following:
Copy code
val expiredClockMark = clock.mark() + timeout
...
if (!expiredClockMark().elapsed.isNegative()) { 
    // expired
}
j
ok. that makes a lot more sense in terms of the behavior i'm seeing then! i need to compare elapsed to my expected duration rather than compare marks
i
We're currently reevaluating ClockMark design to minimize its misuse. Could you help by telling how were your clock mark variables was called originally (if it was different from the second comment), how were you obtaining
expirationClockMark
value and how did your code look in the end after rewriting?
j
Having not read the documentation and going solely based on the API names, I was originally interpreting clock marks as being fixed points on the timeline of the clock such that i could take two of them and then compare them to each other (did one come after another,
clock.mark() > expirationClockMark
) or do math on them (take this mark plus a duration to produce a new mark,
expirationClockMark = currentMark + expirationDuration
). This probably stems from java.time.Clock use which produces Instants which are points on the timeline where this code would have worked. After re-working, based on your help, the final code is at https://github.com/JakeWharton/SdkSearch/blob/master/backend/dac-proxy/src/main/java/com/jakewharton/sdksearch/proxy/memoize.kt
🙏 1
The code was ported from using
clock: () -> Int = System::nanoTime
and
expirationNanos: Int
to
Clock
and
ClockMark
, so that also led me to naively assume they represented the same concepts