8000 Use `Exit.succeed` in more places by kyri-petrou · Pull Request #9418 · zio/zio · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Use Exit.succeed in more places #9418

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 2 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions core-tests/shared/src/test/scala/zio/ZIOSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4650,6 +4650,28 @@ object ZIOSpec extends ZIOBaseSpec {
result <- effects.get
} yield assert(result)(equalTo(List("Closed")))
}
),
suite("Exit")(
test("map returns an Exit") {
val exit = Exit.succeed(1).map(_ + 1)
assertTrue(exit == Exit.Success(2))
},
test("flatMap(success) returns an exit") {
val exit = Exit.succeed(1).flatMap(a => Exit.succeed(a + 1))
assertTrue(exit == Exit.Success(2))
},
test("flatMap(failure) returns an exit") {
val exit = Exit.succeed(1).flatMap(a => Exit.fail(a + 1))
assertTrue(exit == Exit.fail(2))
},
test("fold(success) returns an exit") {
val exit = (Exit.succeed(1): IO[Int, Int]).fold(_ - 1, _ + 1)
assertTrue(exit == Exit.Success(2))
},
test("fold(failure) returns an exit") {
val exit = (Exit.fail(1): IO[Int, Int]).fold(_ - 1, _ + 1)
assertTrue(exit == Exit.Success(0))
}
)
)

Expand Down
34 changes: 10 additions & 24 deletions core/shared/src/main/scala/zio/ZIO.scala
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ sealed trait ZIO[-R, +E, +A]
* Maps the success value of this effect to the specified constant value.
*/
def as[B](b: => B)(implicit trace: Trace): ZIO[R, E, B] =
self.map(_ => b)
self.flatMap(_ => Exit.succeed(b))

/**
* Maps the success value of this effect to a left value.
Expand Down Expand Up @@ -683,15 +683,15 @@ sealed trait ZIO[-R, +E, +A]
* does not fail, but succeeds with the value returned by the left or right
* function passed to `fold`.
*/
def fold[B](failure: E => B, success: A => B)(implicit ev: CanFail[E], trace: Trace): URIO[R, B] =
foldZIO(e => ZIO.succeed(failure(e)), a => ZIO.succeed(success(a)))
final def fold[B](failure: E => B, success: A => B)(implicit ev: CanFail[E], trace: Trace): URIO[R, B] =
foldZIO(e => Exit.succeed(failure(e)), a => Exit.succeed(success(a)))

/**
* A more powerful version of `fold` that allows recovering from any kind of
* failure except external interruption.
*/
def foldCause[B](failure: Cause[E] => B, success: A => B)(implicit trace: Trace): URIO[R, B] =
foldCauseZIO(c => ZIO.succeed(failure(c)), a => ZIO.succeed(success(a)))
foldCauseZIO(c => Exit.succeed(failure(c)), a => Exit.succeed(success(a)))

/**
* A more powerful version of `foldZIO` that allows recovering from any kind
Expand Down Expand Up @@ -969,8 +969,8 @@ sealed trait ZIO[-R, +E, +A]
/**
* Returns an effect whose success is mapped by the specified `f` function.
*/
def map[B](f: A => B)(implicit trace: Trace): ZIO[R, E, B] =
flatMap(a => ZIO.succeed(f(a)))
Copy link
Collaborator

Choose a reason for hiding this comment

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

this will greatly reduce the instances of ZIO.Sync... should be interesting to see if this impacts runtime performance. Does FiberRuntime always prioritize ZIO.flatMap?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

At the moment, the FiberRuntime runloop checks first whether the effect is ZIO.Sync and then Exit. However, after all these changes to use Exit a lot more, I'm contemplating to move Exit to be the first one to be checked. WDUT?

Copy link
Collaborator

Choose a reason for hiding this comment

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

It's hard to say. I figure 90%+ of user code is probably not explicitly using Exit, but I could be wrong. I do think we should generally optimized for deeply nested ZIOs (via map/flatMap) as usercode is often not optimized to reduce that.

It would be cool if we could get stats on suspension depth and ask users to collect this information...

final def map[B](f: A => B)(implicit trace: Trace): ZIO[R, E, B] =
flatMap(a => Exit.succeed(f(a)))

/**
* Returns an effect whose success is mapped by the specified side effecting
Expand All @@ -984,7 +984,7 @@ sealed trait ZIO[-R, +E, +A]
* the specified pair of functions, `f` and `g`.
*/
def mapBoth[E2, B](f: E => E2, g: A => B)(implicit ev: CanFail[E], trace: Trace): ZIO[R, E2, B] =
foldZIO(e => Exit.fail(f(e)), a => ZIO.succeed(g(a)))
foldZIO(e => Exit.fail(f(e)), a => Exit.succeed(g(a)))

/**
* Returns an effect with its error channel mapped using the specified
Expand Down Expand Up @@ -3094,7 +3094,7 @@ object ZIO extends ZIOCompanionPlatformSpecific with ZIOCompanionVersionSpecific
* For effectful conditionals, see [[ZIO.ifZIO]]
*/
def cond[E, A](predicate: => Boolean, result: => A, error: => E)(implicit trace: Trace): IO[E, A] =
ZIO.suspendSucceed(if (predicate) ZIO.succeed(result) else ZIO.fail(error))
ZIO.suspendSucceed(if (predicate) Exit.succeed(result) else ZIO.fail(error))

/**
* Uses the current config provider to load the specified config, or fail with
Expand Down Expand Up @@ -5609,7 +5609,7 @@ object ZIO extends ZIOCompanionPlatformSpecific with ZIOCompanionVersionSpecific

final class EnvironmentWithPartiallyApplied[R](private val dummy: Boolean = true) extends AnyVal {
def apply[A](f: ZEnvironment[R] => A)(implicit trace: Trace): URIO[R, A] =
ZIO.environmentWithZIO(env => ZIO.succeed(f(env)))
ZIO.environmentWithZIO(env => Exit.succeed(f(env)))
}

final class EnvironmentWithZIOPartiallyApplied[R](private val dummy: Boolean = true) extends AnyVal {
Expand Down Expand Up @@ -6145,7 +6145,7 @@ object ZIO extends ZIOCompanionPlatformSpecific with ZIOCompanionVersionSpecific
else MakeUninterruptible

def make(implicit trace: Trace): ZIO[Any, Nothing, InterruptibilityRestorer] =
ZIO.checkInterruptible(status => ZIO.succeed(InterruptibilityRestorer(status)))
ZIO.checkInterruptible(status => Exit.succeed(InterruptibilityRestorer(status)))

case object MakeInterruptible extends InterruptibilityRestorer {
def apply[R, E, A](effect: => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
Expand Down Expand Up @@ -6435,14 +6435,6 @@ sealed trait Exit[+E, +A] extends ZIO[Any, E, A] { self =>
final def flattenExit[E1 >: E, B](implicit ev: A <:< Exit[E1, B]): Exit[E1, B] =
Exit.flatten(self.mapExit(ev))

/**
* Folds over the failure value or the success value to yield an effect that
* does not fail, but succeeds with the value returned by the left or right
* function passed to `fold`.
*/
override final def fold[B](failure: E => B, success: A => B)(implicit ev: CanFail[E], trace: Trace): UIO[B] =
foldZIO(e => Exit.succeed(failure(e)), a => Exit.succeed(success(a)))

/**
* A more powerful version of `fold` that allows recovering from any kind of
* failure except external interruption.
Expand Down Expand Up @@ -6526,12 +6518,6 @@ sealed trait Exit[+E, +A] extends ZIO[Any, E, A] { self =>
final def isSuccess: Boolean =
self.isInstanceOf[Success[?]]

/**
* Returns an effect whose success is mapped by the specified `f` function.
*/
override final def map[B](f: A => B)(implicit trace: Trace): ZIO[Any, E, B] =
mapExit(f)

Comment on lines -6532 to -6534
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@hearnadam since you already reviewed just wanted to point out that I made this change (also with fold) since your last review. I think this overriding is no longer required because we already override flatMap to produce an exit. This allows us to make the map and fold methods final on ZIO which means the JVM can apply better optimizations / inlining as the method is no longer overwritten.

It does result in slightly more allocations in cases that map / fold is used on an Exit, but to be honest I think those cases not very common and we can always use mapExit / foldExit on them.

/**
* Maps over the value type.
*/
Expand Down
4 changes: 3 additions & 1 deletion project/MimaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ object MimaSettings {
exclude[Problem]("zio.Scope#ReleaseMap*"),
exclude[Problem]("zio.Scope$ReleaseMap*"),
exclude[MissingClassProblem]("zio.Scope$Running*"),
exclude[MissingClassProblem]("zio.Scope$Exited*")
exclude[MissingClassProblem]("zio.Scope$Exited*"),
exclude[NewMixinForwarderProblem]("zio.Exit.fold"),
exclude[NewMixinForwarderProblem]("zio.Exit.map")
),
mimaFailOnProblem := failOnProblem
)
Expand Down
Loading
0