8000 Initial Duration module by hearnadam · Pull Request #296 · getkyo/kyo · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Initial Duration module #296

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
May 4, 2024
Merged

Initial Duration module #296

merged 12 commits into from
May 4, 2024

Conversation

hearnadam
Copy link
Collaborator

Needs tests + some minor reworking, but should be usable.

Improves compatibility with ZIO interop and general API interop

import scala.annotation.switch
import scala.concurrent.duration.Duration as SDuration

opaque type Duration <: JDuration = JDuration
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we could use opaque type Duration = Long? It'd be nice to avoid the allocation of Java's wrapper.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible, but my goal was to directly 'replace' Java Duration. Can we make it Java duration compatible - ie, a value class that extends Duration?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, value classes can only extend "universal traits": https://docs.scala-lang.org/overviews/core/value-classes.html.

Maybe a toJava would be enough to provide interop?

Copy link
Collaborator Author
@hearnadam hearnadam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fwbrasil please take a look. Needs some minor cleanup and better tests, but I think this is pretty awesome. Should be fully allocation free.

private[kyo] inline def isFinite: Boolean = self < Duration.Infinity
end extension

private[kyo] object duration:
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't able to find a different way to protect internal implementation details from the rest of the kyo codebase. This leads to some duplication, but it doesn't seem too bad.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What part of the implementation did you want to avoid exposing here? Does anything fail if you move the opaque type to line 10?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is that internal aspects of Kyo's code base become Duration (Longs, Chars, etc) meaning the functions like to are tied to Duration:

This line will cause a failure to compile
https://github.com/getkyo/kyo/blob/main/kyo-core/shared/src/main/scala/kyo/randoms.scala#L54

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's odd, I think the opaque type should be visible only in the same file.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh I see, the issue is the extension methods. Wouldn't that be a problem for users as well?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem was caused by a combination of

extension (sel 10000 f: Duration)
and that
Duration was known throughout the kyo namespace to be a Long. Thus, all Longs got access to internal functions of Durations.

As it stands today, this is not a problem because the Duration type is within its own kyo.duration namespace now.

Comment on lines 23 to 25
// assertTrue(i.days.toDays == i),
// assertTrue(i.weeks.toWeeks == i),
// assertTrue(i.months.toMonths == i)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should provide a clear warning regarding the maximum precision that this can contain. It's more than reasonable for nearly all scheduling tasks...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could fail in case of overflow? Another option is bitwise operations/masks to represent different granularities of durations using a Long but I'm not sure we need that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's proceed with the current module, we can get some feedback from users on if they expect exceptions for overflow. I'm concerned with the safety of random exceptions - but also not excited about silent overflow.

@@ -368,7 +368,6 @@ While the companion object of `KyoApp` provides utility methods to run isolated

```scala
import kyo._
import scala.concurrent.duration._
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not requiring this import is a nice improvement to!

inline def toYears: Long = self.to(Years)

def toScala: ScalaDuration =
(self: @annotation.switch) match
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool, I didn't know this annotation!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it directly compiles match to a java switch. It's a good way to ensure performance, even if the compiler should do the right thing without it. It should fail compilation where the compilation is not possible.

import scala.concurrent.duration.Duration as ScalaDuration

type Duration = duration.Duration
given CanEqual[Duration, Duration] = canEqual
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this to the companion object?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's possible. The type is necessary to be in the top level namespace for the extension methods.

inline def gt(that: Duration): Boolean = self > that
inline def lt(that: Duration): Boolean = self < that
inline def eqEq(that: Duration): Boolean = self == that
inline def neEq(that: Duration): Boolean = self == that
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!=

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch

@hearnadam hearnadam marked this pull request as ready for review May 4, 2024 17:04
@hearnadam hearnadam merged commit 0a0c4c1 into getkyo:main May 4, 2024
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants
0