Thanks to visit codestin.com
Credit goes to github.com

Skip to content
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
67 changes: 66 additions & 1 deletion core/jvm/src/test/scala/zio/IOSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ class IOSpec(implicit ee: org.specs2.concurrent.ExecutionEnv) extends TestRuntim
Check `zipPar` method does not swallow exit causes of loser. $testZipParInterupt
Check `zipPar` method does not report failure when interrupting loser after it succeeded. $testZipParSucceed
Check `orElse` method does not recover from defects. $testOrElseDefectHandling
Check `someOrFail` method extracts the optional value. $testSomeOrFailExtractOptionalValue
Check `someOrFail` method fails when given a None. $testSomeOrFailWithNone
Check `someOrFailException` method extracts the optional value. $testSomeOrFailExceptionOnOptionalValue
Check `someOrFailException` method fails when given a None. $testSomeOrFailExceptionOnEmptyValue
Check `rightOrFail` method extracts the Right value. $testRightOrFailExtractsRightValue
Check `rightOrFail` method fails when given a Left. $testRightOrFailWithLeft
Check `rightOrFailException` method extracts the Right value. $testRightOrFailExceptionOnRightValue
Check `rightOrFailException` method fails when given a Left. $testRightOrFailExceptionOnLeftValue
Check `leftOrFail` method extracts the Left value. $testLeftOrFailExtractsLeftValue
Check `leftOrFail` method fails when given a Right. $testLeftOrFailWithRight
Check `leftOrFailException` method extracts the Left value. $testLeftOrFailExceptionOnLeftValue
Check `leftOrFailException` method fails when given a Right. $testLeftOrFailExceptionOnRightValue
Check uncurried `bracket`. $testUncurriedBracket
Check uncurried `bracket_`. $testUncurriedBracket_
Check uncurried `bracketExit`. $testUncurriedBracketExit
Expand Down Expand Up @@ -134,8 +146,10 @@ class IOSpec(implicit ee: org.specs2.concurrent.ExecutionEnv) extends TestRuntim
(unsafeRunSync(IO.foldLeft(l)(0)((_, _) => IO.fail("fail"))) must_=== unsafeRunSync(IO.fail("fail")))
}

private val exampleError = new Error("something went wrong")

def testDone = {
val error = new Error("something went wrong")
val error = exampleError
val completed = Exit.succeed(1)
val interrupted: Exit[Error, Int] = Exit.interrupt
val terminated: Exit[Error, Int] = Exit.die(error)
Expand Down Expand Up @@ -279,6 +293,57 @@ class IOSpec(implicit ee: org.specs2.concurrent.ExecutionEnv) extends TestRuntim
}
}

def testSomeOrFailWithNone = {
val task: Task[Int] = UIO(Option.empty[Int]).someOrFail(exampleError)
unsafeRun(task) must throwA[FiberFailure]
}

def testSomeOrFailExtractOptionalValue = {
val task: Task[Int] = UIO(Some(42)).someOrFail(exampleError)
unsafeRun(task) must_=== 42
}

def testSomeOrFailExceptionOnOptionalValue = unsafeRun(ZIO.succeed(Some(42)).someOrFailException) must_=== 42

def testSomeOrFailExceptionOnEmptyValue = {
val task = ZIO.succeed(Option.empty[Int]).someOrFailException
unsafeRun(task) must throwA[FiberFailure]
}

def testRightOrFailExceptionOnRightValue = unsafeRun(ZIO.succeed(Right(42)).rightOrFailException) must_=== 42

def testRightOrFailExceptionOnLeftValue = {
val task: Task[Int] = ZIO.succeed(Left(2)).rightOrFailException
unsafeRun(task) must throwA[FiberFailure]
}

def testRightOrFailExtractsRightValue = {
val task: Task[Int] = UIO(Right(42)).rightOrFail(exampleError)
unsafeRun(task) must_=== 42
}

def testRightOrFailWithLeft = {
val task: Task[Int] = UIO(Left(1)).rightOrFail(exampleError)
unsafeRun(task) must throwA[FiberFailure]
}

def testLeftOrFailExceptionOnLeftValue = unsafeRun(ZIO.succeed(Left(42)).leftOrFailException) must_=== 42

def testLeftOrFailExceptionOnRightValue = {
val task: Task[Int] = ZIO.succeed(Right(2)).leftOrFailException
Copy link
Member

Choose a reason for hiding this comment

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

Does this work without annotating task as Task[Int]?

Copy link
Contributor Author

@dorsev dorsev Jul 12, 2019

Choose a reason for hiding this comment

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

No.
The return type by the compiler is [B, C, E1 >:NoSuchElementException]zio.ZIO[Any,E1,B]. 🤔

also, changing the signature to

def leftOrFailException[B, C](
    implicit ev: A <:< Either[B, C]
  ): ZIO[R, NoSuchElementException, B]

does not work too.

Copy link
Member

Choose a reason for hiding this comment

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

Actually, I tested and it looks like it infers properly - scalac says it's a ZIO[Any, NoSuchElementException, Nothing] which is accurate.

unsafeRun(task) must throwA[FiberFailure]
}

def testLeftOrFailExtractsLeftValue = {
val task: Task[Int] = UIO(Left(42)).leftOrFail(exampleError)
unsafeRun(task) must_=== 42
}

def testLeftOrFailWithRight = {
val task: Task[Int] = UIO(Right(12)).leftOrFail(exampleError)
unsafeRun(task) must throwA[FiberFailure]
}

def testUncurriedBracket =
unsafeRun {
for {
Expand Down
16 changes: 16 additions & 0 deletions core/jvm/src/test/scala/zio/RTSSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,14 @@ class RTSSpec(implicit ee: ExecutionEnv) extends TestRuntime {
check interruption regression 1 $testInterruptionRegression1
manual sync interruption $testManualSyncInterruption

RTS option tests
lifting a value to an option $testLiftingOptionalValue
using the none value $testLiftingNoneValue

RTS either helper tests
lifting a value into right $liftValueIntoRight
lifting a value into left $liftValueIntoLeft

RTS interruption
blocking IO is effect blocking $testBlockingIOIsEffectBlocking
sync forever is interruptible $testInterruptSyncForever
Expand Down Expand Up @@ -386,6 +394,14 @@ class RTSSpec(implicit ee: ExecutionEnv) extends TestRuntime {
unsafeRunSync(nested) must_=== Exit.Failure(Then(fail(ExampleError), Then(die(e2), die(e3))))
}

def testLiftingOptionalValue = unsafeRun(ZIO.some(42)) must_=== Some(42)

def testLiftingNoneValue = unsafeRun(ZIO.none) must_=== None

def liftValueIntoRight = unsafeRun(ZIO.right(42)) must_=== Right(42)

def liftValueIntoLeft = unsafeRun(ZIO.left(42)) must_=== Left(42)

def testErrorInFinalizerIsReported = {
@volatile var reported: Exit[Nothing, Int] = null

Expand Down
80 changes: 80 additions & 0 deletions core/shared/src/main/scala/zio/ZIO.scala
Original file line number Diff line number Diff line change
Expand Up @@ -787,8 +787,50 @@ sealed trait ZIO[-R, +E, +A] extends Serializable { self =>

final def left[R1 <: R, C]: ZIO[Either[R1, C], E, Either[A, C]] = self +++ ZIO.identity[C]

/**
* Returns a successful effect if the value is `Left`, or fails with the error e.
*/
def leftOrFail[B, C, E1 >: E](e: E1)(implicit ev: A <:< Either[B, C]): ZIO[R, E1, B] =
self.flatMap(ev(_) match {
case Right(_) => ZIO.fail(e)
case Left(value) => ZIO.succeed(value)
})

/**
* Returns a successful effect if the value is `Left`, or fails with a [[java.util.NoSuchElementException]].
*/
def leftOrFailException[B, C, E1 >: NoSuchElementException](
Copy link
Member

@iravid iravid Jul 12, 2019

Choose a reason for hiding this comment

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

Hmm why E1 >: NoSuchElementException rather than just returning ZIO[R, NoSuchElementException, B]? Since E1 doesn't show up in the function's parameters (other than the ev2 but that doesn't influence inference, I think), this might not infer properly (or worse inferred as Any)

implicit ev: A <:< Either[B, C],
ev2: E <:< E1
): ZIO[R, E1, B] =
self.foldM(
e => ZIO.fail(ev2(e)),
a => ev(a).fold(ZIO.succeed(_), _ => ZIO.fail(new NoSuchElementException("Either.left.get on Right")))
)

final def right[R1 <: R, C]: ZIO[Either[C, R1], E, Either[C, A]] = ZIO.identity[C] +++ self

/**
* Returns a successful effect if the value is `Right`, or fails with the given error 'e'.
*/
def rightOrFail[B, C, E1 >: E](e: E1)(implicit ev: A <:< Either[B, C]): ZIO[R, E1, C] =
self.flatMap(ev(_) match {
case Right(value) => ZIO.succeed(value)
case Left(_) => ZIO.fail(e)
})

/**
* Returns a successful effect if the value is `Right`, or fails with a [[java.util.NoSuchElementException]].
*/
def rightOrFailException[B, C, E1 >: NoSuchElementException](
Copy link
Member

Choose a reason for hiding this comment

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

Same as with leftOrFailException

implicit ev: A <:< Either[B, C],
ev2: E <:< E1
): ZIO[R, E1, C] =
self.foldM(
e => ZIO.fail(ev2(e)),
a => ev(a).fold(_ => ZIO.fail(new NoSuchElementException("Either.right.get on Left")), ZIO.succeed(_))
)

/**
* A variant of `flatMap` that ignores the value produced by this effect.
*/
Expand Down Expand Up @@ -1108,6 +1150,24 @@ sealed trait ZIO[-R, +E, +A] extends Serializable { self =>
*/
final def sandbox: ZIO[R, Cause[E], A] = foldCauseM(ZIO.fail, ZIO.succeed)

/**
* Extracts the optional value, or fails with the given error 'e'.
*/
def someOrFail[B, E1 >: E](e: E1)(implicit ev: A <:< Option[B]): ZIO[R, E1, B] =
self.flatMap(ev(_) match {
case Some(value) => ZIO.succeed(value)
case None => ZIO.fail(e)
})

/**
* Extracts the optional value, or fails with a [[java.util.NoSuchElementException]]
*/
def someOrFailException[B, E1 >: NoSuchElementException](implicit ev: A <:< Option[B], ev2: E <:< E1): ZIO[R, E1, B] =
Copy link
Member

Choose a reason for hiding this comment

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

Same comments as with leftOrFailException

Copy link
Contributor

Choose a reason for hiding this comment

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

This method does almost the same as ZIO#get, do we really need both?

Copy link
Contributor Author

@dorsev dorsev Jul 13, 2019

Choose a reason for hiding this comment

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

@evis, I agree with you.
I think the difference is that: someOrFailException gives you an exception in case of a failure while .get just return the unit value.

what do you think about expressing .get using someOrFailException? does that make sense?

Something like

final def get[E1 >: E, B](implicit ev: A <:< Option[B]): ZIO[R, Unit, B] =
    self.someOrFail(()).mapError(_ => ())

Copy link
Member

Choose a reason for hiding this comment

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

Good idea @dorsev

self.foldM(e => ZIO.fail(ev2(e)), ev(_) match {
case Some(value) => ZIO.succeed(value)
case None => ZIO.fail(new NoSuchElementException("None.get"))
})

/**
* The inverse operation to `sandbox`. Submerges the full cause of failure.
*/
Expand Down Expand Up @@ -1368,6 +1428,11 @@ private[zio] trait ZIOFunctions extends Serializable {
*/
final val interrupt: UIO[Nothing] = haltWith(trace => Cause.Traced(Cause.Interrupt, trace()))

/**
* Returns an effect with the empty value.
*/
final val none: UIO[Option[Nothing]] = succeed(None)

/**
* Returns a effect that will never produce anything. The moral
* equivalent of `while(true) {}`, only without the wasted CPU cycles.
Expand Down Expand Up @@ -1428,6 +1493,11 @@ private[zio] trait ZIOFunctions extends Serializable {
final def forkAll_[R >: LowerR, E <: UpperE, A](as: Iterable[ZIO[R, E, A]]): ZIO[R, Nothing, Unit] =
as.foldRight[ZIO[R, Nothing, Unit]](ZIO.unit)(_.fork *> _)

/**
* Returns an effect with the value on the left part.
*/
final def left[A](a: A): UIO[Either[A, Nothing]] = succeed(Left(a))

/**
* Returns an effect from a [[zio.Exit]] value.
*/
Expand All @@ -1436,6 +1506,11 @@ private[zio] trait ZIOFunctions extends Serializable {
case Exit.Failure(cause) => halt(cause)
}

/**
* Returns an effect with the optional value.
*/
def some[A](a: A): UIO[Option[A]] = succeed(Some(a))

/**
* Enables supervision for this effect. This will cause fibers forked by
* this effect to be tracked and will enable their inspection via [[ZIO.children]].
Expand Down Expand Up @@ -1669,6 +1744,11 @@ private[zio] trait ZIOFunctions extends Serializable {
final def require[E <: UpperE, A](error: E): IO[E, Option[A]] => IO[E, A] =
(io: IO[E, Option[A]]) => io.flatMap(_.fold[IO[E, A]](fail[E](error))(succeed[A]))

/**
* Returns an effect with the value on the right part.
*/
def right[B](b: B): UIO[Either[Nothing, B]] = succeed(Right(b))

/**
* When this effect represents acquisition of a resource (for example,
* opening a file, launching a thread, etc.), `bracket` can be used to ensure
Expand Down