-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Schedule actions at a given time #676
Schedule actions at a given time #676
Conversation
Adding functions enabling an effect to run only at a given point in time. The functions `loop` and `await` call each other to establish when an effect must run. The `loop` function calls `foldM` to execute the effect and retrieve its result. At this point, it gives control to `await` The `await` function keeps passing the last result to schedule and awaits when schedule signal when it's ready to run. Schedule will always receive the last result as parameter to compute the next state. When schedule is ready to run, `await` return control to `loop`, restarting the process Added a new flag to Decision to signal when a schedule is ready to run, not only if it must continue or stop
… parameters to signal if it's ready to run
Changing schedule to receive an unit as input, allowing awaiting since the beginning instead running once before start waiting
Two points on this PR:
|
@@ -71,7 +75,7 @@ trait ZSchedule[-R, -A, +B] extends Serializable { self => | |||
case Nil => IO.succeed(acc) | |||
case a :: as => | |||
self.update(a, s).flatMap { | |||
case ZSchedule.Decision(cont, delay, s, finish) => | |||
case ZSchedule.Decision(cont, _, delay, s, finish) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here i want a suggestion: i'm not considering readiness on run. Should I? If yes, what should be the behavior? Discard results when not ready? Sleep until next readiness?
@alexandredantas I think this is a useful feature to have. However, I think we can do it in a slightly different way that has a cleaner design: adding a method into Then, potentially, every step could return either a delay (relative time) or an instant (absolute time). I have to think this through, but I think this has all the structure required of Do you think that would work? |
@jdegoes make total sense. convert the point at time to a delay and effects would sleep until reaching it. i'll rework on that. thanks for the tip! Does still make sense keeping A very simple way to do it is keeping ps: can you help pointing out which properties i should test? :) |
@alexandredantas The main thing we need to ensure is that invariants around Things will be easier if we introduce a type: sealed trait Delay
object Delay {
final case class Relative(duration: Duration)
final case class Absolute(time: Instant) // Cannot use Instant for Scala.js???
} Now our problem is comparing them to see which one comes first requires an effect, because we have to use the sealed trait Delay {
def < (that: Delay): ZIO[Clock, Nothing, Boolean] = ???
def <= (that: Delay): ZIO[Clock, Nothing, Boolean] = ???
def > (that: Delay): ZIO[Clock, Nothing, Boolean] = ???
def >= (that: Delay): ZIO[Clock, Nothing, Boolean] = ???
def === (that: Delay): ZIO[Clock, Nothing, Boolean] = ???
} Direct comparison on sealed trait DelayComparison { self =>
def choose: ZIO[Clock, Nothing, Delay] =
self match {
??? // Uses effectful comparison methods on `Delay`
}
}
object DelayComparison {
final case class Choose(value: Delay) extends DelayComparison
final case class Min(l: DelayComparison, r: DelayComparison) extends DelayComparison
final case class Max(l: DelayComparison, r: DelayComparison) extends DelayComparison
} Now At this point all existing schedules will be updated to use |
@jdegoes Wow!! Such a very nice solution!! I'll come up with something following your suggestion |
# Conflicts: # core/shared/src/main/scala/scalaz/zio/ZSchedule.scala
Using current time to check which delay to use
# Conflicts: # core/shared/src/main/scala/scalaz/zio/ZSchedule.scala
@jdegoes can you tell me if I'm on the right direction? When I changed Decision.combineWith to (Delay, Delay) => DelayComparison, it was required to touch every other schedule implementation. What did I do wrong? |
Matching DelayComparison on retry/repeat and ZStream to choose which delay to use
@jdegoes After banging my head against wall sometimes, I've came up with this implementation. Adjusted tests accordingly and at least build is passing 😄 |
@alexandredantas After looking at some of the contortions you had to go through, I can see now what I should have seen all alone: sealed trait Delay {
def run: ZIO[Clock, Nothing, Duration, Instant]
}
object Delay {
val none: Delay = Relative(Duration.zero)
// Etc.
final case class Relative(duration: Duration) extends Delay
final case class Absolute(instant: Instant) extends Delay // Scala.js compat?
final case class Min(left: Delay, right: Delay) extends Delay
final case class Max(left: Delay, right: Delay) extends Delay
| Now functions can be |
@jdegoes 😄 Sorry for noobiness. I should have tried that first instead implementing them separately. I'll clean up things and do as you suggested. After all, do things look good? Do you see any major mistake? And about |
@alexandredantas I think a lot of the gnarlier code will be clean after this change. The overall direction of changes seems correct to me. There may be some edge case lodging that needs more careful review when the bigger changes have been made, but you're definitely on the right track! 😄 |
@jdegoes any update? :) |
# Conflicts: # build.sbt # core/shared/src/main/scala/zio/ZSchedule.scala
@alexandredantas How about now? 😄 Did you get everything passing??? |
@jdegoes sorry for not getting the message early. what about friday? anytime :) About the tests, yes, all passing!! I've merged with master and looks like something was fixed |
# Conflicts: # core/shared/src/main/scala/zio/ZSchedule.scala
# Conflicts: # core/jvm/src/test/scala/zio/stm/STMSpec.scala # core/shared/src/main/scala/zio/Schedule.scala # core/shared/src/main/scala/zio/ZIO.scala # streams/shared/src/main/scala/zio/stream/ZStream.scala
…:alexandredantas/scalaz-zio into feature/schedule-actions-at-a-given-time
# Conflicts: # core-tests/jvm/src/test/scala/zio/stm/STMSpec.scala
# Conflicts: # core-tests/shared/src/test/scala/zio/stm/STMSpec.scala
# Conflicts: # core-tests/shared/src/test/scala/zio/QueueSpec.scala # core-tests/shared/src/test/scala/zio/stm/STMSpec.scala # streams/shared/src/main/scala/zio/stream/ZStream.scala
…:alexandredantas/scalaz-zio into feature/schedule-actions-at-a-given-time
This work is being continued in #1798. @alexandredantas Thank you for all your help on this and if you have some time to contribute, your assistance reviewing #1798 and ensuring we satisfy all requirements of the original ticket would be greatly appreciated. 🙏 |
Adding functions enabling an effect to run only at a given point in time.
The functions
exec
andawait
call each other to establish when an effect must run.The
exec
function callsfoldM
to execute the effect and retrieve its result. At this point, it gives control toawait
The
await
function awaits when schedule signal when it's ready to run. Schedule will always receive. Schedules for this always receive unit as input because they don't need effect to run to decide when to runWhen schedule is ready to run,
await
return control toexec
, restarting the process