diff --git a/README.md b/README.md index 7cef337ec0b4..02ff791b9b1d 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ Want to see your company here? [Submit a PR](https://github.com/zio/zio/edit/mas * [Wehkamp](https://www.wehkamp.nl) * [LeadIQ](https://leadiq.com) * [Call Handling](https://www.callhandling.co.uk/) +* [Univalence](https://univalence.io) # Sponsors diff --git a/benchmarks/src/main/scala/zio/chunks/ChunkAddBenchmark.scala b/benchmarks/src/main/scala/zio/chunks/ChunkAppendBenchmark.scala similarity index 77% rename from benchmarks/src/main/scala/zio/chunks/ChunkAddBenchmark.scala rename to benchmarks/src/main/scala/zio/chunks/ChunkAppendBenchmark.scala index 26bb4ac18536..2cae8a12149e 100644 --- a/benchmarks/src/main/scala/zio/chunks/ChunkAddBenchmark.scala +++ b/benchmarks/src/main/scala/zio/chunks/ChunkAppendBenchmark.scala @@ -9,7 +9,7 @@ import zio.Chunk @State(Scope.Thread) @BenchmarkMode(Array(Mode.Throughput)) @OutputTimeUnit(TimeUnit.SECONDS) -class ChunkAddBenchmarks { +class ChunkAppendBenchmarks { val chunk = Chunk(1) val vector = Vector(1) @@ -18,12 +18,12 @@ class ChunkAddBenchmarks { var size: Int = _ @Benchmark - def chunkAdd(): Chunk[Int] = { + def chunkAppend(): Chunk[Int] = { var i = 0 var current = chunk while (i < size) { - current = current :+ 2 + current = current :+ i i += 1 } @@ -31,12 +31,12 @@ class ChunkAddBenchmarks { } @Benchmark - def vectorAdd(): Vector[Int] = { + def vectorAppend(): Vector[Int] = { var i = 0 var current = vector while (i < size) { - current = current :+ 2 + current = current :+ i i += 1 } diff --git a/benchmarks/src/main/scala/zio/chunks/ChunkPrependBenchmark.scala b/benchmarks/src/main/scala/zio/chunks/ChunkPrependBenchmark.scala new file mode 100644 index 000000000000..5445c843fb6f --- /dev/null +++ b/benchmarks/src/main/scala/zio/chunks/ChunkPrependBenchmark.scala @@ -0,0 +1,45 @@ +package zio.chunks + +import java.util.concurrent.TimeUnit + +import org.openjdk.jmh.annotations._ + +import zio.Chunk + +@State(Scope.Thread) +@BenchmarkMode(Array(Mode.Throughput)) +@OutputTimeUnit(TimeUnit.SECONDS) +class ChunkPrependBenchmarks { + + val chunk = Chunk(1) + val vector = Vector(1) + + @Param(Array("10000")) + var size: Int = _ + + @Benchmark + def chunkPrepend(): Chunk[Int] = { + var i = 0 + var current = chunk + + while (i < size) { + current = i +: current + i += 1 + } + + current + } + + @Benchmark + def vectorPrepend(): Vector[Int] = { + var i = 0 + var current = vector + + while (i < size) { + current = i +: current + i += 1 + } + + current + } +} diff --git a/benchmarks/src/main/scala/zio/stm/TReentrantLockBenchmark.scala b/benchmarks/src/main/scala/zio/stm/TReentrantLockBenchmark.scala index 5b82d8065e00..550073e98b84 100644 --- a/benchmarks/src/main/scala/zio/stm/TReentrantLockBenchmark.scala +++ b/benchmarks/src/main/scala/zio/stm/TReentrantLockBenchmark.scala @@ -19,7 +19,7 @@ class TReentrantLockBenchmark { val javaLock = new StampedLock() - val zioLock: ZIO[Any, Nothing, TReentrantLock] = TReentrantLock.make.commit + val zioLock: UIO[TReentrantLock] = TReentrantLock.make.commit @Benchmark @Group("ZioLockBasic") diff --git a/build.sbt b/build.sbt index 475047f0695f..4c602ce90e02 100644 --- a/build.sbt +++ b/build.sbt @@ -327,9 +327,9 @@ lazy val benchmarks = project.module Seq( "co.fs2" %% "fs2-core" % "2.3.0", "com.google.code.findbugs" % "jsr305" % "3.0.2", - "com.twitter" %% "util-core" % "20.4.1", + "com.twitter" %% "util-core" % "20.5.0", "com.typesafe.akka" %% "akka-stream" % "2.6.5", - "io.monix" %% "monix" % "3.2.1", + "io.monix" %% "monix" % "3.2.2", "io.projectreactor" % "reactor-core" % "3.3.5.RELEASE", "io.reactivex.rxjava2" % "rxjava" % "2.2.19", "org.ow2.asm" % "asm" % "8.0.1", @@ -371,7 +371,7 @@ lazy val docs = project.module scalacOptions ~= { _ filterNot (_ startsWith "-Xlint") }, libraryDependencies ++= Seq( "com.github.ghik" % "silencer-lib" % "1.4.4" % Provided cross CrossVersion.full, - "commons-io" % "commons-io" % "2.6" % "provided", + "commons-io" % "commons-io" % "2.7" % "provided", "org.jsoup" % "jsoup" % "1.13.1" % "provided", "org.reactivestreams" % "reactive-streams-examples" % "1.0.3" % "provided", "dev.zio" %% "zio-interop-cats" % "2.0.0.0-RC13", @@ -379,7 +379,7 @@ lazy val docs = project.module "dev.zio" %% "zio-interop-monix" % "3.0.0.0-RC7", "dev.zio" %% "zio-interop-scalaz7x" % "7.2.27.0-RC9", "dev.zio" %% "zio-interop-java" % "1.1.0.0-RC6", - "dev.zio" %% "zio-interop-reactivestreams" % "1.0.3.5-RC8", + "dev.zio" %% "zio-interop-reactivestreams" % "1.0.3.5-RC10", "dev.zio" %% "zio-interop-twitter" % "19.7.0.0-RC2" ) ) diff --git a/core-tests/jvm/src/test/scala-2.12/zio/StacktracesSpec.scala b/core-tests/jvm/src/test/scala-2.12/zio/StacktracesSpec.scala index 3f3e018967b3..f2564c8c36cd 100644 --- a/core-tests/jvm/src/test/scala-2.12/zio/StacktracesSpec.scala +++ b/core-tests/jvm/src/test/scala-2.12/zio/StacktracesSpec.scala @@ -165,7 +165,7 @@ object StackTracesSpec extends DefaultRunnableSpec { } }, testM("blocking trace") { - val io: ZIO[Blocking, Throwable, Unit] = for { + val io: RIO[Blocking, Unit] = for { trace <- blockingTrace } yield trace @@ -310,13 +310,13 @@ object StackTracesSpec extends DefaultRunnableSpec { def show(cause: Cause[Any]): Unit = if (debug) println(cause.prettyPrint) - def basicTest: ZIO[Any, Nothing, ZTrace] = + def basicTest: UIO[ZTrace] = for { _ <- ZIO.unit trace <- ZIO.trace } yield trace - def foreachTest: ZIO[Any, Nothing, ZTrace] = { + def foreachTest: UIO[ZTrace] = { import foreachTraceFixture._ for { _ <- effectTotal @@ -342,7 +342,7 @@ object StackTracesSpec extends DefaultRunnableSpec { t2 <- ZIO.trace } yield (t1, t2) - def foreachParFail: ZIO[Any, Nothing, Unit] = + def foreachParFail: UIO[Unit] = for { _ <- ZIO.foreachPar(1 to 10)(i => (if (i >= 7) UIO(i / 0) else UIO(i / 10))) } yield () @@ -352,7 +352,7 @@ object StackTracesSpec extends DefaultRunnableSpec { _ <- ZIO.foreachParN(4)(1 to 10)(i => (if (i >= 7) UIO(i / 0) else UIO(i / 10))) } yield () - def leftAssociativeFold(n: Int): ZIO[Any, Nothing, ZTrace] = + def leftAssociativeFold(n: Int): UIO[ZTrace] = (1 to n) .foldLeft(ZIO.unit *> ZIO.unit) { (acc, _) => acc *> UIO(()) diff --git a/core-tests/jvm/src/test/scala/zio/CancelableFutureSpecJVM.scala b/core-tests/jvm/src/test/scala/zio/CancelableFutureSpecJVM.scala new file mode 100644 index 000000000000..92125521b047 --- /dev/null +++ b/core-tests/jvm/src/test/scala/zio/CancelableFutureSpecJVM.scala @@ -0,0 +1,39 @@ +package zio + +import java.util.concurrent.Executors + +import scala.concurrent.ExecutionContext + +import zio.duration._ +import zio.internal.Executor +import zio.test.Assertion._ +import zio.test.TestAspect._ +import zio.test._ + +object CancelableFutureSpecJVM extends ZIOBaseSpec { + + import ZIOTag._ + + def spec = + suite("CancelableFutureSpecJVM")( + testM("fromFuture/unsafeRunToFuture doesn't deadlock") { + + val tst = + for { + runtime <- ZIO.runtime[Any] + r <- ZIO.fromFuture(_ => runtime.unsafeRunToFuture(UIO.succeedNow(0))) + } yield assert(r)(equalTo(0)) + ZIO + .runtime[Any] + .map( + _.mapPlatform( + _.withExecutor( + Executor.fromExecutionContext(1)( + ExecutionContext.fromExecutor(Executors.newSingleThreadScheduledExecutor()) + ) + ) + ).unsafeRun(tst) + ) + } @@ timeout(1.second) + ) @@ zioTag(future) +} diff --git a/core-tests/jvm/src/test/scala/zio/interop/JavaSpec.scala b/core-tests/jvm/src/test/scala/zio/interop/JavaSpec.scala index 98fd62d3a6ac..b6956bec0ea7 100644 --- a/core-tests/jvm/src/test/scala/zio/interop/JavaSpec.scala +++ b/core-tests/jvm/src/test/scala/zio/interop/JavaSpec.scala @@ -59,7 +59,7 @@ object JavaSpec extends ZIOBaseSpec { testM("catch exceptions thrown by lazy block") { val ex = new Exception("no future for you!") lazy val noFuture: CompletionStage[Unit] = throw ex - assertM(ZIO.fromCompletionStage(noFuture).run)(dies(equalTo(ex))) + assertM(ZIO.fromCompletionStage(noFuture).run)(fails(equalTo(ex))) } @@ zioTag(errors), testM("return an `IO` that fails if `Future` fails (failedFuture)") { val ex = new Exception("no value for you!") @@ -78,7 +78,15 @@ object JavaSpec extends ZIOBaseSpec { testM("handle null produced by the completed `Future`") { lazy val someValue: CompletionStage[String] = CompletableFuture.completedFuture[String](null) assertM(ZIO.fromCompletionStage(someValue).map(Option(_)))(isNone) - } @@ zioTag(errors) + } @@ zioTag(errors), + testM("be referentially transparent") { + var n = 0 + val task = ZIO.fromCompletionStage(CompletableFuture.supplyAsync(() => n += 1)) + for { + _ <- task + _ <- task + } yield assert(n)(equalTo(2)) + } ) @@ zioTag(future), suite("`Task.toCompletableFuture` must")( testM("produce always a successful `IO` of `Future`") { @@ -123,7 +131,7 @@ object JavaSpec extends ZIOBaseSpec { testM("catch exceptions thrown by lazy block") { val ex = new Exception("no future for you!") def noFuture: CompletionStage[Unit] = throw ex - assertM(Fiber.fromCompletionStage(noFuture).join.run)(dies(equalTo(ex))) + assertM(Fiber.fromCompletionStage(noFuture).join.run)(fails(equalTo(ex))) } @@ zioTag(errors), testM("return an `IO` that fails if `Future` fails (failedFuture)") { val ex = new Exception("no value for you!") diff --git a/core-tests/shared/src/test/scala/zio/BracketTypeInferrenceSpec.scala b/core-tests/shared/src/test/scala/zio/BracketTypeInferrenceSpec.scala index 8fb5acf14660..a0970a7b6592 100644 --- a/core-tests/shared/src/test/scala/zio/BracketTypeInferrenceSpec.scala +++ b/core-tests/shared/src/test/scala/zio/BracketTypeInferrenceSpec.scala @@ -10,16 +10,16 @@ object BracketTypeInferenceSpec { class E1 extends E def infersEType1: ZIO[R, E, B] = { - val acquire: ZIO[R, E, A] = ??? - val release: A => ZIO[R, Nothing, Any] = ??? - val use: A => ZIO[R, E1, B] = ??? + val acquire: ZIO[R, E, A] = ??? + val release: A => URIO[R, Any] = ??? + val use: A => ZIO[R, E1, B] = ??? acquire.bracket(release)(use) } def infersEType2: ZIO[R, E, B] = { - val acquire: ZIO[R, E1, A] = ??? - val release: A => ZIO[R, Nothing, Any] = ??? - val use: A => ZIO[R, E, B] = ??? + val acquire: ZIO[R, E1, A] = ??? + val release: A => URIO[R, Any] = ??? + val use: A => ZIO[R, E, B] = ??? acquire.bracket(release, use) } diff --git a/core-tests/shared/src/test/scala/zio/CancelableFutureSpec.scala b/core-tests/shared/src/test/scala/zio/CancelableFutureSpec.scala index ecd30f012f84..c2b443ba705e 100644 --- a/core-tests/shared/src/test/scala/zio/CancelableFutureSpec.scala +++ b/core-tests/shared/src/test/scala/zio/CancelableFutureSpec.scala @@ -44,6 +44,14 @@ object CancelableFutureSpec extends ZIOBaseSpec { assertM(Live.live(result.timeout(1.seconds)))(isNone) } @@ zioTag(supervision, regression), + testM("unsafeRunToFuture interruptibility") { + for { + runtime <- ZIO.runtime[Any] + f = runtime.unsafeRunToFuture(UIO.never) + _ <- UIO(f.cancel()) + r <- ZIO.fromFuture(_ => f).run + } yield assert(r.succeeded)(isFalse) // not interrupted, as the Future fails when the effect in interrupted. + } @@ timeout(1.second) @@ jvmOnly @@ zioTag(interruption), testM("roundtrip preserves interruptibility") { for { start <- Promise.make[Nothing, Unit] diff --git a/core-tests/shared/src/test/scala/zio/ChunkSpec.scala b/core-tests/shared/src/test/scala/zio/ChunkSpec.scala index 923d67c94a2d..f7f9b3049906 100644 --- a/core-tests/shared/src/test/scala/zio/ChunkSpec.scala +++ b/core-tests/shared/src/test/scala/zio/ChunkSpec.scala @@ -57,7 +57,7 @@ object ChunkSpec extends ZIOBaseSpec { assert(chunk.size)(equalTo(chunk.length)) } ), - suite("add")( + suite("append")( testM("apply") { val chunksWithIndex: Gen[Random with Sized, (Chunk[Int], Chunk[Int], Int)] = for { @@ -104,6 +104,53 @@ object ChunkSpec extends ZIOBaseSpec { } } ), + suite("prepend")( + testM("apply") { + val chunksWithIndex: Gen[Random with Sized, (Chunk[Int], Chunk[Int], Int)] = + for { + p <- Gen.boolean + as <- Gen.chunkOf(Gen.anyInt) + bs <- Gen.chunkOf1(Gen.anyInt) + n <- Gen.int(0, as.length + bs.length - 1) + } yield if (p) (as, bs, n) else (bs, as, n) + check(chunksWithIndex) { + case (as, bs, n) => + val actual = as.foldRight(bs)(_ +: _).apply(n) + val expected = (as ++ bs).apply(n) + assert(actual)(equalTo(expected)) + } + }, + testM("buffer full") { + check(Gen.chunkOf(Gen.anyInt), Gen.chunkOf(Gen.anyInt)) { (as, bs) => + def addAll[A](l: Chunk[A], r: Chunk[A]): Chunk[A] = l.foldRight(r)(_ +: _) + val actual = List.fill(100)(as).foldRight(bs)(addAll) + val expected = List.fill(100)(as).foldRight(bs)(_ ++ _) + assert(actual)(equalTo(expected)) + } + }, + testM("buffer used") { + checkM(Gen.chunkOf(Gen.anyInt), Gen.chunkOf(Gen.anyInt)) { (as, bs) => + val effect = ZIO.succeed(as.foldRight(bs)(_ +: _)) + val actual = ZIO.collectAllPar(ZIO.replicate(100)(effect)) + val expected = (as ++ bs) + assertM(actual)(forall(equalTo(expected))) + } + }, + testM("equals") { + check(Gen.chunkOf(Gen.anyInt), Gen.chunkOf(Gen.anyInt)) { (as, bs) => + val actual = as.foldRight(bs)(_ +: _) + val expected = (as ++ bs) + assert(actual)(equalTo(expected)) + } + }, + testM("length") { + check(Gen.chunkOf(Gen.anyInt), smallChunks(Gen.anyInt)) { (as, bs) => + val actual = as.foldRight(bs)(_ +: _).length + val expected = (as ++ bs).length + assert(actual)(equalTo(expected)) + } + } + ), testM("apply") { check(chunkWithIndex(Gen.unit)) { case (chunk, i) => diff --git a/core-tests/shared/src/test/scala/zio/ZIOSpec.scala b/core-tests/shared/src/test/scala/zio/ZIOSpec.scala index c9054092cd0e..8dec392d243c 100644 --- a/core-tests/shared/src/test/scala/zio/ZIOSpec.scala +++ b/core-tests/shared/src/test/scala/zio/ZIOSpec.scala @@ -1438,7 +1438,7 @@ object ZIOSpec extends ZIOBaseSpec { testM("provides the part of the environment that is not part of the `ZEnv`") { val loggingLayer: ZLayer[Any, Nothing, Logging] = Logging.live val zio: ZIO[ZEnv with Logging, Nothing, Unit] = ZIO.unit - val zio2: ZIO[ZEnv, Nothing, Unit] = zio.provideCustomLayer(loggingLayer) + val zio2: URIO[ZEnv, Unit] = zio.provideCustomLayer(loggingLayer) assertM(zio2)(anything) } ), @@ -1446,7 +1446,7 @@ object ZIOSpec extends ZIOBaseSpec { testM("can split environment into two parts") { val clockLayer: ZLayer[Any, Nothing, Clock] = Clock.live val zio: ZIO[Clock with Random, Nothing, Unit] = ZIO.unit - val zio2: ZIO[Random, Nothing, Unit] = zio.provideSomeLayer[Random](clockLayer) + val zio2: URIO[Random, Unit] = zio.provideSomeLayer[Random](clockLayer) assertM(zio2)(anything) } ), @@ -2966,6 +2966,17 @@ object ZIOSpec extends ZIOBaseSpec { assert(conditionVal2)(equalTo(2)) && assert(failed)(isLeft(equalTo(failure))) } + }, + testM("infers correctly") { + trait R + trait R1 extends R + trait E1 + trait E extends E1 + trait A + val b: ZIO[R, E, Boolean] = ZIO.succeed(true) + val zio: ZIO[R1, E1, A] = ZIO.succeed(new A {}) + val _ = ZIO.unlessM(b)(zio) + ZIO.succeed(assertCompletes) } ), suite("unrefine")( @@ -3192,6 +3203,17 @@ object ZIOSpec extends ZIOBaseSpec { assert(conditionVal2)(equalTo(2)) && assert(failed)(isLeft(equalTo(failure))) } + }, + testM("infers correctly") { + trait R + trait R1 extends R + trait E1 + trait E extends E1 + trait A + val b: ZIO[R, E, Boolean] = ZIO.succeed(true) + val zio: ZIO[R1, E1, A] = ZIO.succeed(new A {}) + val _ = ZIO.whenM(b)(zio) + ZIO.succeed(assertCompletes) } ), suite("withFilter")( @@ -3222,7 +3244,9 @@ object ZIOSpec extends ZIOBaseSpec { } yield n """ } - val expected = "Cannot prove that NoSuchElementException <:< String." + + val expected = + "Pattern guards are only supported when the error type is a supertype of NoSuchElementException. However, your effect has String for the error type." if (TestVersion.isScala2) assertM(result)(isLeft(equalTo(expected))) else assertM(result)(isLeft(anything)) } @@ -3247,7 +3271,14 @@ object ZIOSpec extends ZIOBaseSpec { result <- ZIO.fromFuture(_ => future).either } yield assert(result)(isLeft(hasThrowableCause(hasThrowableCause(hasMessage(containsString("Fiber:Id(")))))) } - ) @@ zioTag(future) + ) @@ zioTag(future), + suite("resurrect")( + testM("should fail checked") { + val error: Exception = new Exception("msg") + val effect: Task[Unit] = ZIO.fail(error).unit.orDie.resurrect + assertM(effect.either)(isLeft(equalTo(error))) + } + ) ) def functionIOGen: Gen[Random with Sized, String => Task[Int]] = diff --git a/core-tests/shared/src/test/scala/zio/ZManagedSpec.scala b/core-tests/shared/src/test/scala/zio/ZManagedSpec.scala index c5675566e9f0..4ab8b29e2a63 100644 --- a/core-tests/shared/src/test/scala/zio/ZManagedSpec.scala +++ b/core-tests/shared/src/test/scala/zio/ZManagedSpec.scala @@ -60,7 +60,7 @@ object ZManagedSpec extends ZIOBaseSpec { def acquire1: ZIO[R, E, A] = ??? def acquire2: ZIO[R1, E, A] = ??? def acquire3: ZIO[R2, E, A] = ??? - def release1: A => ZIO[R, Nothing, Any] = ??? + def release1: A => URIO[R, Any] = ??? def release2: A => ZIO[R1, Nothing, Any] = ??? def release3: A => ZIO[R2, Nothing, Any] = ??? def managed1: ZManaged[R with R1, E, A] = ZManaged.make(acquire1)(release2) @@ -876,9 +876,9 @@ object ZManagedSpec extends ZIOBaseSpec { val expected = Chunk("acquiring a", "acquiring b", "releasing b", "acquiring c", "releasing c", "releasing a") for { ref <- Ref.make[Chunk[String]](Chunk.empty) - a = Managed.make(ref.update(_ + "acquiring a"))(_ => ref.update(_ + "releasing a")) - b = Managed.make(ref.update(_ + "acquiring b"))(_ => ref.update(_ + "releasing b")) - c = Managed.make(ref.update(_ + "acquiring c"))(_ => ref.update(_ + "releasing c")) + a = Managed.make(ref.update(_ :+ "acquiring a"))(_ => ref.update(_ :+ "releasing a")) + b = Managed.make(ref.update(_ :+ "acquiring b"))(_ => ref.update(_ :+ "releasing b")) + c = Managed.make(ref.update(_ :+ "acquiring c"))(_ => ref.update(_ :+ "releasing c")) managed = a *> b.release *> c _ <- managed.useNow log <- ref.get @@ -1509,7 +1509,7 @@ object ZManagedSpec extends ZIOBaseSpec { val managed = ZManaged.succeed(42).collectM("Oh No!") { case 42 => ZManaged.succeed(84) } - val effect: ZIO[Any, String, Int] = managed.use(ZIO.succeed(_)) + val effect: IO[String, Int] = managed.use(ZIO.succeed(_)) assertM(effect)(equalTo(84)) }, @@ -1517,7 +1517,7 @@ object ZManagedSpec extends ZIOBaseSpec { val managed = ZManaged.succeed(42).collectM("Oh No!") { case 43 => ZManaged.succeed(84) } - val effect: ZIO[Any, String, Int] = managed.use(ZIO.succeed(_)) + val effect: IO[String, Int] = managed.use(ZIO.succeed(_)) assertM(effect.run)(fails(equalTo("Oh No!"))) } diff --git a/core-tests/shared/src/test/scala/zio/stm/ZSTMSpec.scala b/core-tests/shared/src/test/scala/zio/stm/ZSTMSpec.scala index 69eb124b0954..7a5418bf847c 100644 --- a/core-tests/shared/src/test/scala/zio/stm/ZSTMSpec.scala +++ b/core-tests/shared/src/test/scala/zio/stm/ZSTMSpec.scala @@ -1284,9 +1284,9 @@ object ZSTMSpec extends ZIOBaseSpec { assertM(chain(10000)(_.foldM(_ => STM.succeed(0), a => STM.succeed(a + 1))))(equalTo(10000)) }, testM("long mapError chains") { - def chain(depth: Int): ZIO[Any, Int, Nothing] = { + def chain(depth: Int): IO[Int, Nothing] = { @annotation.tailrec - def loop(n: Int, acc: STM[Int, Nothing]): ZIO[Any, Int, Nothing] = + def loop(n: Int, acc: STM[Int, Nothing]): IO[Int, Nothing] = if (n <= 0) acc.commit else loop(n - 1, acc.mapError(_ + 1)) loop(depth, STM.fail(0)) diff --git a/core/js/src/main/scala/zio/App.scala b/core/js/src/main/scala/zio/App.scala index e2b6c678ce97..83fd9baa75b1 100644 --- a/core/js/src/main/scala/zio/App.scala +++ b/core/js/src/main/scala/zio/App.scala @@ -22,7 +22,7 @@ trait App extends BootstrapRuntime { * The main function of the application, which will be passed the command-line * arguments to the program. */ - def run(args: List[String]): ZIO[ZEnv, Nothing, ExitCode] + def run(args: List[String]): URIO[ZEnv, ExitCode] /** * The Scala main function, intended to be called only by the Scala runtime. diff --git a/core/jvm/src/main/scala/zio/App.scala b/core/jvm/src/main/scala/zio/App.scala index 261345fbbad3..32e628a91fd0 100644 --- a/core/jvm/src/main/scala/zio/App.scala +++ b/core/jvm/src/main/scala/zio/App.scala @@ -43,7 +43,7 @@ trait App extends BootstrapRuntime { * The main function of the application, which will be passed the command-line * arguments to the program and has to return an `IO` with the errors fully handled. */ - def run(args: List[String]): ZIO[ZEnv, Nothing, ExitCode] + def run(args: List[String]): URIO[ZEnv, ExitCode] /** * The Scala main function, intended to be called only by the Scala runtime. diff --git a/core/jvm/src/main/scala/zio/blocking/package.scala b/core/jvm/src/main/scala/zio/blocking/package.scala index 7c3686f31c5e..bc20216070d1 100644 --- a/core/jvm/src/main/scala/zio/blocking/package.scala +++ b/core/jvm/src/main/scala/zio/blocking/package.scala @@ -34,17 +34,17 @@ package object blocking { object Blocking extends Serializable { trait Service extends Serializable { - /** - * Retrieves the executor for all blocking tasks. - */ - def blockingExecutor: Executor - /** * Locks the specified effect to the blocking thread pool. */ def blocking[R, E, A](zio: ZIO[R, E, A]): ZIO[R, E, A] = zio.lock(blockingExecutor) + /** + * Retrieves the executor for all blocking tasks. + */ + def blockingExecutor: Executor + /** * Imports a synchronous effect that does blocking IO into a pure value. */ @@ -142,6 +142,13 @@ package object blocking { ) } } + + /** + * Imports a synchronous effect that does blocking IO into a pure value, + * refining the error type to `[[java.io.IOException]]`. + */ + def effectBlockingIO[A](effect: => A): ZIO[Blocking, IOException, A] = + effectBlocking(effect).refineToOrDie[IOException] } object Service { @@ -157,21 +164,53 @@ package object blocking { ZLayer.succeed(Service.live) } + /** + * Locks the specified effect to the blocking thread pool. + */ def blocking[R <: Blocking, E, A](zio: ZIO[R, E, A]): ZIO[R, E, A] = - ZIO.accessM[R](_.get.blocking(zio)) + ZIO.accessM(_.get.blocking(zio)) - def effectBlocking[A](effect: => A): ZIO[Blocking, Throwable, A] = - ZIO.accessM[Blocking](_.get.effectBlocking(effect)) + /** + * Retrieves the executor for all blocking tasks. + */ + def blockingExecutor: URIO[Blocking, Executor] = + ZIO.access(_.get.blockingExecutor) - def effectBlockingCancelable[A](effect: => A)(cancel: UIO[Unit]): ZIO[Blocking, Throwable, A] = - ZIO.accessM[Blocking](_.get.effectBlockingCancelable(effect)(cancel)) + /** + * Retrieves the executor for all blocking tasks. + */ + def effectBlocking[A](effect: => A): RIO[Blocking, A] = + ZIO.accessM(_.get.effectBlocking(effect)) - def effectBlockingIO[A](effect: => A): ZIO[Blocking, IOException, A] = - effectBlocking(effect).refineToOrDie[IOException] + /** + * Imports a synchronous effect that does blocking IO into a pure value, with + * a custom cancel effect. + * + * If the returned `ZIO` is interrupted, the blocked thread running the + * synchronous effect will be interrupted via the cancel effect. + */ + def effectBlockingCancelable[A](effect: => A)(cancel: UIO[Unit]): RIO[Blocking, A] = + ZIO.accessM(_.get.effectBlockingCancelable(effect)(cancel)) - def effectBlockingInterrupt[A](effect: => A): ZIO[Blocking, Throwable, A] = + /** + * Imports a synchronous effect that does blocking IO into a pure value. + * + * If the returned `ZIO` is interrupted, the blocked thread running the + * synchronous effect will be interrupted via `Thread.interrupt`. + * + * Note that this adds significant overhead. For performance sensitive + * applications consider using `effectBlocking` or `effectBlockingCancel`. + */ + def effectBlockingInterrupt[A](effect: => A): RIO[Blocking, A] = ZIO.accessM(_.get.effectBlockingInterrupt(effect)) + /** + * Imports a synchronous effect that does blocking IO into a pure value, + * refining the error type to `[[java.io.IOException]]`. + */ + def effectBlockingIO[A](effect: => A): ZIO[Blocking, IOException, A] = + ZIO.accessM(_.get.effectBlockingIO(effect)) + private[blocking] object internal { private[blocking] val blockingExecutor0 = Executor.fromThreadPoolExecutor(_ => Int.MaxValue) { diff --git a/core/jvm/src/main/scala/zio/interop/javaz.scala b/core/jvm/src/main/scala/zio/interop/javaz.scala index 8a6774b0851e..69fd38fd9a57 100644 --- a/core/jvm/src/main/scala/zio/interop/javaz.scala +++ b/core/jvm/src/main/scala/zio/interop/javaz.scala @@ -61,34 +61,33 @@ private[zio] object javaz { Task.succeedNow(f.get()) } catch catchFromGet(isFatal) - def fromCompletionStage[A](thunk: => CompletionStage[A]): Task[A] = { - lazy val cs: CompletionStage[A] = thunk - Task.effectSuspendTotalWith { (p, _) => - val cf = cs.toCompletableFuture - if (cf.isDone) { - unwrapDone(p.fatal)(cf) - } else { - Task.effectAsync { cb => - cs.handle[Unit] { (v: A, t: Throwable) => - val io = Option(t).fold[Task[A]](Task.succeed(v)) { t => - catchFromGet(p.fatal).lift(t).getOrElse(Task.die(t)) + def fromCompletionStage[A](thunk: => CompletionStage[A]): Task[A] = + Task.effect(thunk).flatMap { cs => + Task.effectSuspendTotalWith { (p, _) => + val cf = cs.toCompletableFuture + if (cf.isDone) { + unwrapDone(p.fatal)(cf) + } else { + Task.effectAsync { cb => + cs.handle[Unit] { (v: A, t: Throwable) => + val io = Option(t).fold[Task[A]](Task.succeed(v)) { t => + catchFromGet(p.fatal).lift(t).getOrElse(Task.die(t)) + } + cb(io) } - cb(io) } } } } - } /** WARNING: this uses the blocking Future#get, consider using `fromCompletionStage` */ def fromFutureJava[A](future: => Future[A]): RIO[Blocking, A] = RIO.effectSuspendTotalWith { (p, _) => - lazy val lazyFuture: Future[A] = future - - if (lazyFuture.isDone) { - unwrapDone(p.fatal)(lazyFuture) + val capturedFuture: Future[A] = future + if (capturedFuture.isDone) { + unwrapDone(p.fatal)(capturedFuture) } else { - blocking(Task.effectSuspend(unwrapDone(p.fatal)(lazyFuture))) + blocking(Task.effectSuspend(unwrapDone(p.fatal)(capturedFuture))) } } diff --git a/core/native/src/main/scala/zio/App.scala b/core/native/src/main/scala/zio/App.scala index e2b6c678ce97..83fd9baa75b1 100644 --- a/core/native/src/main/scala/zio/App.scala +++ b/core/native/src/main/scala/zio/App.scala @@ -22,7 +22,7 @@ trait App extends BootstrapRuntime { * The main function of the application, which will be passed the command-line * arguments to the program. */ - def run(args: List[String]): ZIO[ZEnv, Nothing, ExitCode] + def run(args: List[String]): URIO[ZEnv, ExitCode] /** * The Scala main function, intended to be called only by the Scala runtime. diff --git a/core/shared/src/main/scala-2.11-2.12/ChunkLike.scala b/core/shared/src/main/scala-2.11-2.12/ChunkLike.scala index 5af05778ddc2..29bf7a5a65e5 100644 --- a/core/shared/src/main/scala-2.11-2.12/ChunkLike.scala +++ b/core/shared/src/main/scala-2.11-2.12/ChunkLike.scala @@ -44,6 +44,12 @@ private[zio] trait ChunkLike[+A] extends IndexedSeq[A] with IndexedSeqLike[A, Ch case _ => super.:+(a1) } + override final def +:[A1 >: A, That](a1: A1)(implicit bf: CanBuildFrom[Chunk[A], A1, That]): That = + bf match { + case _: ChunkCanBuildFrom[A1] => prepend(a1) + case _ => super.:+(a1) + } + /** * Returns a filtered, mapped subset of the elements of this chunk. */ diff --git a/core/shared/src/main/scala-2.13+/ChunkLike.scala b/core/shared/src/main/scala-2.13+/ChunkLike.scala index 8cf250df70b0..7ad48f637975 100644 --- a/core/shared/src/main/scala-2.13+/ChunkLike.scala +++ b/core/shared/src/main/scala-2.13+/ChunkLike.scala @@ -44,6 +44,9 @@ trait ChunkLike[+A] override final def appended[A1 >: A](a1: A1): Chunk[A1] = append(a1) + override final def prepended[A1 >: A](a1: A1): Chunk[A1] = + prepend(a1) + /** * Returns a filtered, mapped subset of the elements of this `Chunk`. */ diff --git a/core/shared/src/main/scala/zio/Chunk.scala b/core/shared/src/main/scala/zio/Chunk.scala index d6ce60920b3b..203e1f104d24 100644 --- a/core/shared/src/main/scala/zio/Chunk.scala +++ b/core/shared/src/main/scala/zio/Chunk.scala @@ -37,9 +37,9 @@ sealed trait Chunk[+A] extends ChunkLike[A] { self => /** * Appends an element to the chunk */ + @deprecated("use :+", "1.0.0") final def +[A1 >: A](a: A1): Chunk[A1] = - if (self.length == 0) Chunk.single(a) - else Chunk.Concat(self, Chunk.single(a)) + self :+ a /** * Returns the concatenation of this chunk with the specified chunk. @@ -52,13 +52,6 @@ sealed trait Chunk[+A] extends ChunkLike[A] { self => final def ++[A1 >: A](that: NonEmptyChunk[A1]): NonEmptyChunk[A1] = that.prepend(self) - /** - * Appends an element to the chunk - */ - protected def append[A1 >: A](a: A1): Chunk[A1] = - if (self.length == 0) Chunk.single(a) - else Chunk.Concat(self, Chunk.single(a)) - /** * Converts a chunk of bytes to a chunk of bits. */ @@ -729,6 +722,20 @@ sealed trait Chunk[+A] extends ChunkLike[A] { self => protected[zio] def toArray[A1 >: A](n: Int, dest: Array[A1]): Unit = if (isEmpty) () else materialize.toArray(n, dest) + /** + * Appends an element to the chunk. + */ + protected def append[A1 >: A](a: A1): Chunk[A1] = + if (self.length == 0) Chunk.single(a) + else Chunk.Concat(self, Chunk.single(a)) + + /** + * Prepends an element to the chunk. + */ + protected def prepend[A1 >: A](a: A1): Chunk[A1] = + if (self.length == 0) Chunk.single(a) + else Chunk.Concat(Chunk.single(a), self) + /** * Returns a filtered, mapped subset of the elements of this chunk. */ @@ -938,6 +945,7 @@ object Chunk { case x: Arr[A] => x.classTag case x: Concat[A] => x.classTag case Empty => classTag[java.lang.Object].asInstanceOf[ClassTag[A]] + case x: PrependN[A] => x.classTag case x: Singleton[A] => x.classTag case x: Slice[A] => x.classTag case x: VectorChunk[A] => x.classTag @@ -977,6 +985,34 @@ object Chunk { } } + private final case class PrependN[A](end: Chunk[A], buffer: Array[AnyRef], bufferUsed: Int, chain: AtomicInteger) + extends Chunk[A] { self => + + implicit val classTag: ClassTag[A] = classTagOf(end) + + val length: Int = + end.length + bufferUsed + + override protected def prepend[A1 >: A](a1: A1): Chunk[A1] = + if (bufferUsed < buffer.length && chain.compareAndSet(bufferUsed, bufferUsed + 1)) { + buffer(BufferSize - bufferUsed - 1) = a1.asInstanceOf[AnyRef] + PrependN(end, buffer, bufferUsed + 1, chain) + } else { + val buffer = Array.ofDim[AnyRef](BufferSize) + buffer(BufferSize - 1) = a1.asInstanceOf[AnyRef] + PrependN(self, buffer, 1, new AtomicInteger(1)) + } + + def apply(n: Int): A = + if (n < bufferUsed) buffer(BufferSize - bufferUsed + n).asInstanceOf[A] else end(n - bufferUsed) + + override protected[zio] def toArray[A1 >: A](n: Int, dest: Array[A1]): Unit = { + val length = math.min(bufferUsed, math.max(dest.length - n, 0)) + Array.copy(buffer, BufferSize - bufferUsed, dest, n, length) + val _ = end.toArray(n + length, dest) + } + } + private[zio] sealed abstract class Arr[A] extends Chunk[A] with Serializable { self => val array: Array[A] @@ -987,12 +1023,6 @@ object Chunk { override val length: Int = array.length - override protected def append[A1 >: A](a1: A1): Chunk[A] = { - val buffer = Array.ofDim[AnyRef](BufferSize) - buffer(0) = a1.asInstanceOf[AnyRef] - AppendN(self, buffer, 1, new AtomicInteger(1)) - } - override def apply(n: Int): A = array(n) @@ -1132,6 +1162,18 @@ object Chunk { override def materialize[A1 >: A]: Chunk[A1] = self + override protected def append[A1 >: A](a1: A1): Chunk[A] = { + val buffer = Array.ofDim[AnyRef](BufferSize) + buffer(0) = a1.asInstanceOf[AnyRef] + AppendN(self, buffer, 1, new AtomicInteger(1)) + } + + override protected def prepend[A1 >: A](a1: A1): Chunk[A] = { + val buffer = Array.ofDim[AnyRef](BufferSize) + buffer(BufferSize - 1) = a1.asInstanceOf[AnyRef] + PrependN(self, buffer, 1, new AtomicInteger(1)) + } + /** * Takes all elements so long as the predicate returns true. */ diff --git a/core/shared/src/main/scala/zio/Fiber.scala b/core/shared/src/main/scala/zio/Fiber.scala index 635aa4e5dff9..36439227d3fb 100644 --- a/core/shared/src/main/scala/zio/Fiber.scala +++ b/core/shared/src/main/scala/zio/Fiber.scala @@ -412,7 +412,7 @@ sealed trait Fiber[+E, +A] { self => object Fiber extends FiberPlatformSpecific { /** - * A runtime fiber that is executing an effect. Runtime fibers ave an + * A runtime fiber that is executing an effect. Runtime fibers have an * identity and a trace. */ sealed trait Runtime[+E, +A] extends Fiber[E, A] { self => diff --git a/core/shared/src/main/scala/zio/IO.scala b/core/shared/src/main/scala/zio/IO.scala index 98e44d2a25d7..8a931c1940c7 100644 --- a/core/shared/src/main/scala/zio/IO.scala +++ b/core/shared/src/main/scala/zio/IO.scala @@ -518,7 +518,7 @@ object IO { * @see [[zio.ZIO.ifM]] */ def ifM[E](b: IO[E, Boolean]): ZIO.IfM[Any, E] = - new ZIO.IfM(b) + ZIO.ifM(b) /** * @see See [[zio.ZIO.interrupt]] @@ -759,8 +759,8 @@ object IO { /** * @see See [[zio.ZIO.unlessM]] */ - def unlessM[E](b: IO[E, Boolean])(zio: => IO[E, Any]): IO[E, Unit] = - ZIO.unlessM(b)(zio) + def unlessM[E](b: IO[E, Boolean]): ZIO.UnlessM[Any, E] = + ZIO.unlessM(b) /** * @see See [[zio.ZIO.unsandbox]] @@ -817,8 +817,8 @@ object IO { /** * @see See [[zio.ZIO.whenM]] */ - def whenM[E](b: IO[E, Boolean])(io: => IO[E, Any]): IO[E, Unit] = - ZIO.whenM(b)(io) + def whenM[E](b: IO[E, Boolean]): ZIO.WhenM[Any, E] = + ZIO.whenM(b) /** * @see See [[zio.ZIO.yieldNow]] diff --git a/core/shared/src/main/scala/zio/ManagedApp.scala b/core/shared/src/main/scala/zio/ManagedApp.scala index 063cb4da8cfa..fab88bf6413b 100644 --- a/core/shared/src/main/scala/zio/ManagedApp.scala +++ b/core/shared/src/main/scala/zio/ManagedApp.scala @@ -25,7 +25,7 @@ trait ManagedApp extends BootstrapRuntime { ma => def run(args: List[String]): ZManaged[ZEnv, Nothing, ExitCode] private val app = new App { - override def run(args: List[String]): ZIO[ZEnv, Nothing, ExitCode] = + override def run(args: List[String]): URIO[ZEnv, ExitCode] = ma.run(args).use(exit => ZIO.effectTotal(exit)) } diff --git a/core/shared/src/main/scala/zio/RIO.scala b/core/shared/src/main/scala/zio/RIO.scala index fb118cecaafd..c1258064edc6 100644 --- a/core/shared/src/main/scala/zio/RIO.scala +++ b/core/shared/src/main/scala/zio/RIO.scala @@ -59,7 +59,7 @@ object RIO { */ def bracket[R, A, B]( acquire: RIO[R, A], - release: A => ZIO[R, Nothing, Any], + release: A => URIO[R, Any], use: A => RIO[R, B] ): RIO[R, B] = ZIO.bracket(acquire, release, use) @@ -74,7 +74,7 @@ object RIO { */ def bracketExit[R, A, B]( acquire: RIO[R, A], - release: (A, Exit[Throwable, B]) => ZIO[R, Nothing, Any], + release: (A, Exit[Throwable, B]) => URIO[R, Any], use: A => RIO[R, B] ): RIO[R, B] = ZIO.bracketExit(acquire, release, use) @@ -319,7 +319,7 @@ object RIO { /** * @see See [[zio.ZIO.environment]] */ - def environment[R]: ZIO[R, Nothing, R] = ZIO.environment + def environment[R]: URIO[R, R] = ZIO.environment /** * @see See [[zio.ZIO.fail]] @@ -467,7 +467,7 @@ object RIO { /** * @see See [[zio.ZIO.forkAll_]] */ - def forkAll_[R, A](as: Iterable[RIO[R, A]]): ZIO[R, Nothing, Unit] = + def forkAll_[R, A](as: Iterable[RIO[R, A]]): URIO[R, Unit] = ZIO.forkAll_(as) /** @@ -549,7 +549,7 @@ object RIO { * @see [[zio.ZIO.ifM]] */ def ifM[R](b: RIO[R, Boolean]): ZIO.IfM[R, Throwable] = - new ZIO.IfM(b) + ZIO.ifM(b) /** * @see [[zio.ZIO.infinity]] @@ -841,8 +841,8 @@ object RIO { /** * @see See [[zio.ZIO.unlessM]] */ - def unlessM[R](b: RIO[R, Boolean])(zio: => RIO[R, Any]): RIO[R, Unit] = - ZIO.unlessM(b)(zio) + def unlessM[R](b: RIO[R, Boolean]): ZIO.UnlessM[R, Throwable] = + ZIO.unlessM(b) /** * @see See [[zio.ZIO.unsandbox]] @@ -875,8 +875,8 @@ object RIO { /** * @see See [[zio.ZIO.whenM]] */ - def whenM[R](b: RIO[R, Boolean])(rio: => RIO[R, Any]): RIO[R, Unit] = - ZIO.whenM(b)(rio) + def whenM[R](b: RIO[R, Boolean]): ZIO.WhenM[R, Throwable] = + ZIO.whenM(b) /** * @see See [[zio.ZIO.yieldNow]] diff --git a/core/shared/src/main/scala/zio/Runtime.scala b/core/shared/src/main/scala/zio/Runtime.scala index d44d3931e626..49efd66a6eee 100644 --- a/core/shared/src/main/scala/zio/Runtime.scala +++ b/core/shared/src/main/scala/zio/Runtime.scala @@ -16,6 +16,8 @@ package zio +import scala.concurrent.Future + import zio.internal.Tracing import zio.internal.tracing.{ TracingConfig, ZIOFn } import zio.internal.{ Executor, FiberContext, Platform, PlatformConstants } @@ -65,7 +67,7 @@ trait Runtime[+R] { * * This method is effectful and should only be done at the edges of your program. */ - final def unsafeRunTask[A](task: => ZIO[R, Throwable, A]): A = + final def unsafeRunTask[A](task: => RIO[R, A]): A = unsafeRunSync(task).fold(cause => throw cause.squashTrace, identity) /** @@ -89,6 +91,18 @@ trait Runtime[+R] { * This method is effectful and should only be invoked at the edges of your program. */ final def unsafeRunAsync[E, A](zio: => ZIO[R, E, A])(k: Exit[E, A] => Any): Unit = { + unsafeRunAsyncCancelable(zio)(k) + () + } + + /** + * Executes the effect asynchronously, + * eventually passing the exit value to the specified callback. + * It returns a callback, which can be used to interrupt the running execution. + * + * This method is effectful and should only be invoked at the edges of your program. + */ + final def unsafeRunAsyncCancelable[E, A](zio: => ZIO[R, E, A])(k: Exit[E, A] => Any): Fiber.Id => Exit[E, A] = { val InitialInterruptStatus = InterruptStatus.Interruptible val fiberId = Fiber.newFiberId() @@ -109,7 +123,7 @@ trait Runtime[+R] { context.evaluateNow(ZIOFn.recordStackTrace(() => zio)(zio.asInstanceOf[IO[E, A]])) context.runAsync(k) - () + fiberId => unsafeRun(context.interruptAs(fiberId)) } /** @@ -125,8 +139,13 @@ trait Runtime[+R] { * * This method is effectful and should only be used at the edges of your program. */ - final def unsafeRunToFuture[E <: Throwable, A](zio: ZIO[R, E, A]): CancelableFuture[A] = - unsafeRun(zio.forkDaemon >>= (_.toFuture)) + final def unsafeRunToFuture[E <: Throwable, A](zio: ZIO[R, E, A]): CancelableFuture[A] = { + val p: concurrent.Promise[A] = scala.concurrent.Promise[A]() + val canceler = unsafeRunAsyncCancelable(zio)(_.fold(cause => p.failure(cause.squashTraceWith(identity)), p.success)) + new CancelableFuture[A](p.future) { + def cancel(): Future[Exit[Throwable, A]] = Future.successful(canceler(Fiber.Id.None)) + } + } /** * Constructs a new `Runtime` with the specified new environment. diff --git a/core/shared/src/main/scala/zio/Schedule.scala b/core/shared/src/main/scala/zio/Schedule.scala index 386e49533c6c..f638e2cb5aab 100644 --- a/core/shared/src/main/scala/zio/Schedule.scala +++ b/core/shared/src/main/scala/zio/Schedule.scala @@ -51,7 +51,7 @@ trait Schedule[-R, -A, +B] extends Serializable { self => /** * The initial state of the schedule. */ - val initial: ZIO[R, Nothing, State] + val initial: URIO[R, State] /** * Extract the B from the schedule @@ -671,8 +671,8 @@ trait Schedule[-R, -A, +B] extends Serializable { self => object Schedule { def apply[R, S, A, B]( - initial0: ZIO[R, Nothing, S], - update0: (A, S) => ZIO[R, Nothing, S], + initial0: URIO[R, S], + update0: (A, S) => URIO[R, S], extract0: (A, S) => B ): Schedule[R, A, B] = new Schedule[R, A, B] { @@ -959,14 +959,14 @@ object Schedule { * A schedule that recurs forever, dumping input values to the specified * sink, and returning those same values unmodified. */ - def tapInput[R, A](f: A => ZIO[R, Nothing, Unit]): Schedule[R, A, A] = + def tapInput[R, A](f: A => URIO[R, Unit]): Schedule[R, A, A] = identity[A].tapInput(f) /** * A schedule that recurs forever, dumping output values to the specified * sink, and returning those same values unmodified. */ - def tapOutput[R, A](f: A => ZIO[R, Nothing, Unit]): Schedule[R, A, A] = + def tapOutput[R, A](f: A => URIO[R, Unit]): Schedule[R, A, A] = identity[A].tapOutput(f) /** @@ -980,6 +980,6 @@ object Schedule { * A schedule that always recurs without delay, and computes the output * through recured application of a function to a base value. */ - def unfoldM[R, A](a: ZIO[R, Nothing, A])(f: A => ZIO[R, Nothing, A]): Schedule[R, Any, A] = + def unfoldM[R, A](a: URIO[R, A])(f: A => URIO[R, A]): Schedule[R, Any, A] = Schedule[R, A, Any, A](a, (_, a) => f(a), (_, a) => a) } diff --git a/core/shared/src/main/scala/zio/Task.scala b/core/shared/src/main/scala/zio/Task.scala index ffaa52977ae3..7b541e6bec00 100644 --- a/core/shared/src/main/scala/zio/Task.scala +++ b/core/shared/src/main/scala/zio/Task.scala @@ -518,7 +518,7 @@ object Task extends TaskPlatformSpecific { * @see [[zio.ZIO.ifM]] */ def ifM(b: Task[Boolean]): ZIO.IfM[Any, Throwable] = - new ZIO.IfM(b) + ZIO.ifM(b) /** * @see See [[zio.ZIO.interrupt]] @@ -754,8 +754,8 @@ object Task extends TaskPlatformSpecific { /** * @see See [[zio.ZIO.unlessM]] */ - def unlessM(b: Task[Boolean])(zio: => Task[Any]): Task[Unit] = - ZIO.unlessM(b)(zio) + def unlessM(b: Task[Boolean]): ZIO.UnlessM[Any, Throwable] = + ZIO.unlessM(b) /** * @see [[zio.ZIO.unsandbox]] @@ -788,8 +788,8 @@ object Task extends TaskPlatformSpecific { /** * @see See [[zio.ZIO.whenM]] */ - def whenM(b: Task[Boolean])(task: => Task[Any]): Task[Unit] = - ZIO.whenM(b)(task) + def whenM(b: Task[Boolean]): ZIO.WhenM[Any, Throwable] = + ZIO.whenM(b) /** * @see See [[zio.ZIO.yieldNow]] diff --git a/core/shared/src/main/scala/zio/UIO.scala b/core/shared/src/main/scala/zio/UIO.scala index 0b71ec50edc5..d1a6bb9fbc07 100644 --- a/core/shared/src/main/scala/zio/UIO.scala +++ b/core/shared/src/main/scala/zio/UIO.scala @@ -458,7 +458,7 @@ object UIO { * @see [[zio.ZIO.ifM]] */ def ifM(b: UIO[Boolean]): ZIO.IfM[Any, Nothing] = - new ZIO.IfM(b) + ZIO.ifM(b) /** * @see See [[zio.ZIO.interrupt]] @@ -668,8 +668,8 @@ object UIO { /** * @see See [[zio.ZIO.unlessM]] */ - def unlessM(b: UIO[Boolean])(zio: => UIO[Any]): UIO[Unit] = - ZIO.unlessM(b)(zio) + def unlessM(b: UIO[Boolean]): ZIO.UnlessM[Any, Nothing] = + ZIO.unlessM(b) /** * @see [[zio.ZIO.unsandbox]] @@ -702,8 +702,8 @@ object UIO { /** * @see See [[zio.ZIO.whenM]] */ - def whenM(b: UIO[Boolean])(uio: => UIO[Any]): UIO[Unit] = - ZIO.whenM(b)(uio) + def whenM(b: UIO[Boolean]): ZIO.WhenM[Any, Nothing] = + ZIO.whenM(b) /** * @see See [[zio.ZIO.yieldNow]] diff --git a/core/shared/src/main/scala/zio/URIO.scala b/core/shared/src/main/scala/zio/URIO.scala index 3f6456ad6732..ad07b235a07d 100644 --- a/core/shared/src/main/scala/zio/URIO.scala +++ b/core/shared/src/main/scala/zio/URIO.scala @@ -296,7 +296,7 @@ object URIO { /** * @see [[zio.ZIO.environment]] */ - def environment[R]: ZIO[R, Nothing, R] = ZIO.environment + def environment[R]: URIO[R, R] = ZIO.environment /** * @see [[zio.ZIO.fiberId]] @@ -439,7 +439,7 @@ object URIO { /** * @see [[zio.ZIO.forkAll_]] */ - def forkAll_[R, A](as: Iterable[URIO[R, A]]): ZIO[R, Nothing, Unit] = + def forkAll_[R, A](as: Iterable[URIO[R, A]]): URIO[R, Unit] = ZIO.forkAll_(as) /** @@ -492,7 +492,7 @@ object URIO { * @see [[zio.ZIO.ifM]] */ def ifM[R](b: URIO[R, Boolean]): ZIO.IfM[R, Nothing] = - new ZIO.IfM(b) + ZIO.ifM(b) /** * @see [[zio.ZIO.infinity]] @@ -757,8 +757,8 @@ object URIO { /** * @see See [[zio.ZIO.unlessM]] */ - def unlessM[R](b: URIO[R, Boolean])(zio: => URIO[R, Any]): URIO[R, Unit] = - ZIO.unlessM(b)(zio) + def unlessM[R](b: URIO[R, Boolean]): ZIO.UnlessM[R, Nothing] = + ZIO.unlessM(b) /** * @see [[zio.ZIO.unsandbox]] @@ -790,7 +790,8 @@ object URIO { /** * @see [[zio.ZIO.whenM]] */ - def whenM[R](b: URIO[R, Boolean])(rio: => URIO[R, Any]): URIO[R, Unit] = ZIO.whenM(b)(rio) + def whenM[R](b: URIO[R, Boolean]): ZIO.WhenM[R, Nothing] = + ZIO.whenM(b) /** * @see [[zio.ZIO.yieldNow]] diff --git a/core/shared/src/main/scala/zio/ZIO.scala b/core/shared/src/main/scala/zio/ZIO.scala index 6826381f22cb..e981ab878d13 100644 --- a/core/shared/src/main/scala/zio/ZIO.scala +++ b/core/shared/src/main/scala/zio/ZIO.scala @@ -16,6 +16,7 @@ package zio +import scala.annotation.implicitNotFound import scala.concurrent.ExecutionContext import scala.reflect.ClassTag import scala.util.{ Failure, Success } @@ -169,14 +170,14 @@ sealed trait ZIO[-R, +E, +A] extends Serializable with ZIOPlatformSpecific[R, E, * Attempts to convert defects into a failure, throwing away all information * about the cause of the failure. */ - final def absorb(implicit ev: E <:< Throwable): ZIO[R, Throwable, A] = + final def absorb(implicit ev: E <:< Throwable): RIO[R, A] = absorbWith(ev) /** * Attempts to convert defects into a failure, throwing away all information * about the cause of the failure. */ - final def absorbWith(f: E => Throwable): ZIO[R, Throwable, A] = + final def absorbWith(f: E => Throwable): RIO[R, A] = self.sandbox .foldM( cause => ZIO.fail(cause.squashWith(f)), @@ -832,13 +833,13 @@ sealed trait ZIO[-R, +E, +A] extends Serializable with ZIOPlatformSpecific[R, E, /** * Returns whether this effect is a failure. */ - final def isFailure: ZIO[R, Nothing, Boolean] = + final def isFailure: URIO[R, Boolean] = fold(_ => true, _ => false) /** * Returns whether this effect is a success. */ - final def isSuccess: ZIO[R, Nothing, Boolean] = + final def isSuccess: URIO[R, Boolean] = fold(_ => false, _ => true) /** @@ -872,7 +873,7 @@ sealed trait ZIO[-R, +E, +A] extends Serializable with ZIOPlatformSpecific[R, E, * Returns an effect whose success is mapped by the specified side effecting * `f` function, translating any thrown exceptions into typed failed effects. */ - final def mapEffect[B](f: A => B)(implicit ev: E <:< Throwable): ZIO[R, Throwable, B] = + final def mapEffect[B](f: A => B)(implicit ev: E <:< Throwable): RIO[R, B] = foldM(e => ZIO.fail(ev(e)), a => ZIO.effect(f(a))) /** @@ -1039,7 +1040,18 @@ sealed trait ZIO[-R, +E, +A] extends Serializable with ZIOPlatformSpecific[R, E, * the specified function to convert the `E` into a `Throwable`. */ final def orDieWith(f: E => Throwable)(implicit ev: CanFail[E]): URIO[R, A] = - (self mapError f) catchAll (IO.die(_)) + self.foldM(e => ZIO.die(f(e)), ZIO.succeedNow) + + /** + * Unearth the unchecked failure of the effect. (opposite of `orDie`) + * {{{ + * val f0: Task[Unit] = ZIO.fail(new Exception("failing")).unit + * val f1: UIO[Unit]  = f0.orDie + * val f2: Task[Unit] = f1.resurrect + * }}} + */ + final def resurrect(implicit ev1: E <:< Throwable): RIO[R, A] = + self.unrefineWith({ case e => e })(ev1) /** * Executes this effect and returns its value, if it succeeds, but @@ -1339,8 +1351,8 @@ sealed trait ZIO[-R, +E, +A] extends Serializable with ZIOPlatformSpecific[R, E, new ZIO.RaceWith[R1, E, E1, E2, A, B, C]( self, that, - (exit, fiber) => leftDone(exit, fiber), - (exit, fiber) => rightDone(exit, fiber) + (leftExit, rightFiber) => leftDone(leftExit, rightFiber), + (rightExit, leftFiber) => rightDone(rightExit, leftFiber) ) /** @@ -3521,8 +3533,8 @@ object ZIO extends ZIOCompanionPlatformSpecific { /** * The moral equivalent of `if (!p) exp` when `p` has side-effects */ - def unlessM[R, E](b: ZIO[R, E, Boolean])(zio: => ZIO[R, E, Any]): ZIO[R, E, Unit] = - b.flatMap(b => if (b) unit else zio.unit) + def unlessM[R, E](b: ZIO[R, E, Boolean]): ZIO.UnlessM[R, E] = + new ZIO.UnlessM(b) /** * The inverse operation `IO.sandboxed` @@ -3610,8 +3622,8 @@ object ZIO extends ZIOCompanionPlatformSpecific { /** * The moral equivalent of `if (p) exp` when `p` has side-effects */ - def whenM[R, E](b: ZIO[R, E, Boolean])(zio: => ZIO[R, E, Any]): ZIO[R, E, Unit] = - b.flatMap(b => if (b) zio.unit else unit) + def whenM[R, E](b: ZIO[R, E, Boolean]): ZIO.WhenM[R, E] = + new ZIO.WhenM(b) /** * Returns an effect that yields to the runtime system, starting on a fresh @@ -3669,6 +3681,20 @@ object ZIO extends ZIOCompanionPlatformSpecific { self.provideLayer[E1, R0, R0 with R1](ZLayer.identity[R0] ++ layer) } + @implicitNotFound( + "Pattern guards are only supported when the error type is a supertype of NoSuchElementException. However, your effect has ${E} for the error type." + ) + sealed trait CanFilter[+E] { + def apply(t: NoSuchElementException): E + } + + object CanFilter { + implicit def canFilter[E >: NoSuchElementException]: CanFilter[E] = + new CanFilter[E] { + def apply(t: NoSuchElementException): E = t + } + } + implicit final class ZIOWithFilterOps[R, E, A](private val self: ZIO[R, E, A]) extends AnyVal { /** @@ -3680,10 +3706,10 @@ object ZIO extends ZIOCompanionPlatformSpecific { * positive <- io2 if positive > 0 * } yield () */ - def withFilter(predicate: A => Boolean)(implicit ev: NoSuchElementException <:< E): ZIO[R, E, A] = + def withFilter(predicate: A => Boolean)(implicit ev: CanFilter[E]): ZIO[R, E, A] = self.flatMap { a => if (predicate(a)) ZIO.succeedNow(a) - else ZIO.fail(new NoSuchElementException("The value doesn't satisfy the predicate")) + else ZIO.fail(ev(new NoSuchElementException("The value doesn't satisfy the predicate"))) } } @@ -3707,6 +3733,16 @@ object ZIO extends ZIOCompanionPlatformSpecific { b.flatMap(b => if (b) onTrue else onFalse) } + final class UnlessM[R, E](private val b: ZIO[R, E, Boolean]) extends AnyVal { + def apply[R1 <: R, E1 >: E](zio: => ZIO[R1, E1, Any]): ZIO[R1, E1, Unit] = + b.flatMap(b => if (b) unit else zio.unit) + } + + final class WhenM[R, E](private val b: ZIO[R, E, Boolean]) extends AnyVal { + def apply[R1 <: R, E1 >: E](zio: => ZIO[R1, E1, Any]): ZIO[R1, E1, Unit] = + b.flatMap(b => if (b) zio.unit else unit) + } + final class TimeoutTo[-R, +E, +A, +B](self: ZIO[R, E, A], b: B) { def apply[B1 >: B](f: A => B1)(duration: Duration): ZIO[R with Clock, E, B1] = (self map f) raceFirst (ZIO.sleep(duration).interruptible as b) diff --git a/core/shared/src/main/scala/zio/ZLayer.scala b/core/shared/src/main/scala/zio/ZLayer.scala index 78baa4f99a06..b20841e9e4b8 100644 --- a/core/shared/src/main/scala/zio/ZLayer.scala +++ b/core/shared/src/main/scala/zio/ZLayer.scala @@ -2161,14 +2161,14 @@ object ZLayer { /** * Constructs a layer from the specified value. */ - def succeed[A: Tag](a: => A): Layer[Nothing, Has[A]] = + def succeed[A: Tag](a: => A): ULayer[Has[A]] = ZLayer(ZManaged.succeed(Has(a))) /** * Constructs a layer from the specified value, which must return one or more * services. */ - def succeedMany[A](a: => A): Layer[Nothing, A] = + def succeedMany[A](a: => A): ULayer[A] = ZLayer(ZManaged.succeed(a)) implicit final class ZLayerPassthroughOps[RIn, E, ROut](private val self: ZLayer[RIn, E, ROut]) extends AnyVal { diff --git a/core/shared/src/main/scala/zio/ZManaged.scala b/core/shared/src/main/scala/zio/ZManaged.scala index 5ca6ac2920fc..57ab46b1e06e 100644 --- a/core/shared/src/main/scala/zio/ZManaged.scala +++ b/core/shared/src/main/scala/zio/ZManaged.scala @@ -30,7 +30,7 @@ import zio.duration.Duration * * See [[ZManaged#reserve]] and [[ZIO#reserve]] for details of usage. */ -final case class Reservation[-R, +E, +A](acquire: ZIO[R, E, A], release: Exit[Any, Any] => ZIO[R, Nothing, Any]) +final case class Reservation[-R, +E, +A](acquire: ZIO[R, E, A], release: Exit[Any, Any] => URIO[R, Any]) /** * A `ZManaged[R, E, A]` is a managed resource of type `A`, which may be used by @@ -1147,6 +1147,16 @@ object ZManaged { self.provideLayer[E1, R0, R0 with R1](ZLayer.identity[R0] ++ layer) } + final class UnlessM[R, E](private val b: ZManaged[R, E, Boolean]) extends AnyVal { + def apply[R1 <: R, E1 >: E](managed: => ZManaged[R1, E1, Any]): ZManaged[R1, E1, Unit] = + b.flatMap(b => if (b) unit else managed.unit) + } + + final class WhenM[R, E](private val b: ZManaged[R, E, Boolean]) extends AnyVal { + def apply[R1 <: R, E1 >: E](managed: => ZManaged[R1, E1, Any]): ZManaged[R1, E1, Unit] = + b.flatMap(b => if (b) managed.unit else unit) + } + /** * A `ReleaseMap` represents the finalizers associated with a scope. * @@ -1494,14 +1504,14 @@ object ZManaged { * Creates an effect that only executes the provided finalizer as its * release action. */ - def finalizer[R](f: ZIO[R, Nothing, Any]): ZManaged[R, Nothing, Unit] = + def finalizer[R](f: URIO[R, Any]): ZManaged[R, Nothing, Unit] = finalizerExit(_ => f) /** * Creates an effect that only executes the provided function as its * release action. */ - def finalizerExit[R](f: Exit[Any, Any] => ZIO[R, Nothing, Any]): ZManaged[R, Nothing, Unit] = + def finalizerExit[R](f: Exit[Any, Any] => URIO[R, Any]): ZManaged[R, Nothing, Unit] = makeExit(ZIO.unit)((_, e) => f(e)) /** @@ -1816,7 +1826,7 @@ object ZManaged { */ def makeInterruptible[R, E, A]( acquire: ZIO[R, E, A] - )(release: A => ZIO[R, Nothing, Any]): ZManaged[R, E, A] = + )(release: A => URIO[R, Any]): ZManaged[R, E, A] = ZManaged.fromEffect(acquire).onExitFirst(_.foreach(release)) /** @@ -2203,8 +2213,8 @@ object ZManaged { /** * The moral equivalent of `if (!p) exp` when `p` has side-effects */ - def unlessM[R, E](b: ZManaged[R, E, Boolean])(zio: => ZManaged[R, E, Any]): ZManaged[R, E, Unit] = - b.flatMap(b => if (b) unit else zio.unit) + def unlessM[R, E](b: ZManaged[R, E, Boolean]): ZManaged.UnlessM[R, E] = + new ZManaged.UnlessM(b) /** * The inverse operation to `sandbox`. Submerges the full cause of failure. @@ -2241,8 +2251,8 @@ object ZManaged { /** * The moral equivalent of `if (p) exp` when `p` has side-effects */ - def whenM[R, E](b: ZManaged[R, E, Boolean])(zManaged: => ZManaged[R, E, Any]): ZManaged[R, E, Unit] = - b.flatMap(b => if (b) zManaged.unit else unit) + def whenM[R, E](b: ZManaged[R, E, Boolean]): ZManaged.WhenM[R, E] = + new ZManaged.WhenM(b) private[zio] def succeedNow[A](r: A): ZManaged[Any, Nothing, A] = ZManaged(IO.succeedNow((Finalizer.noop, r))) diff --git a/core/shared/src/main/scala/zio/clock/package.scala b/core/shared/src/main/scala/zio/clock/package.scala index 5d08ed7f47bb..92a2e57d463f 100644 --- a/core/shared/src/main/scala/zio/clock/package.scala +++ b/core/shared/src/main/scala/zio/clock/package.scala @@ -70,7 +70,7 @@ package object clock { /** * Returns the current time, relative to the Unix epoch. */ - def currentTime(unit: => TimeUnit): ZIO[Clock, Nothing, Long] = + def currentTime(unit: => TimeUnit): URIO[Clock, Long] = ZIO.accessM(_.get.currentTime(unit)) /** @@ -82,13 +82,13 @@ package object clock { /** * Returns the system nano time, which is not relative to any date. */ - val nanoTime: ZIO[Clock, Nothing, Long] = + val nanoTime: URIO[Clock, Long] = ZIO.accessM(_.get.nanoTime) /** * Sleeps for the specified duration. This is always asynchronous. */ - def sleep(duration: => Duration): ZIO[Clock, Nothing, Unit] = + def sleep(duration: => Duration): URIO[Clock, Unit] = ZIO.accessM(_.get.sleep(duration)) } diff --git a/core/shared/src/main/scala/zio/console/package.scala b/core/shared/src/main/scala/zio/console/package.scala index 581a9dfc677b..94858ed6d45c 100644 --- a/core/shared/src/main/scala/zio/console/package.scala +++ b/core/shared/src/main/scala/zio/console/package.scala @@ -61,7 +61,7 @@ package object console { /** * Retrieves a line of input from the console. */ - final val getStrLn: ZIO[Any, IOException, String] = + final val getStrLn: IO[IOException, String] = getStrLn(SConsole.in) /** @@ -91,13 +91,13 @@ package object console { /** * Prints text to the console. */ - def putStr(line: => String): ZIO[Console, Nothing, Unit] = + def putStr(line: => String): URIO[Console, Unit] = ZIO.accessM(_.get putStr line) /** * Prints a line of text to the console, including a newline character. */ - def putStrLn(line: => String): ZIO[Console, Nothing, Unit] = + def putStrLn(line: => String): URIO[Console, Unit] = ZIO.accessM(_.get putStrLn line) /** diff --git a/core/shared/src/main/scala/zio/internal/package.scala b/core/shared/src/main/scala/zio/internal/package.scala index 15766f646ad7..c96a66fd1493 100644 --- a/core/shared/src/main/scala/zio/internal/package.scala +++ b/core/shared/src/main/scala/zio/internal/package.scala @@ -23,7 +23,7 @@ package object internal { /** * Returns an effect that models success with the specified value. */ - def ZIOSucceedNow[A](a: A): ZIO[Any, Nothing, A] = + def ZIOSucceedNow[A](a: A): UIO[A] = ZIO.succeedNow(a) /** diff --git a/core/shared/src/main/scala/zio/package.scala b/core/shared/src/main/scala/zio/package.scala index 1c4993bd78f0..95ce245aaf9d 100644 --- a/core/shared/src/main/scala/zio/package.scala +++ b/core/shared/src/main/scala/zio/package.scala @@ -19,17 +19,17 @@ package object zio extends EitherCompat with PlatformSpecific with VersionSpecif type Canceler[-R] = URIO[R, Any] - type RIO[-R, +A] = ZIO[R, Throwable, A] - type URIO[-R, +A] = ZIO[R, Nothing, A] - type IO[+E, +A] = ZIO[Any, E, A] - type UIO[+A] = ZIO[Any, Nothing, A] - type Task[+A] = ZIO[Any, Throwable, A] - - type RManaged[-R, +A] = ZManaged[R, Throwable, A] - type URManaged[-R, +A] = ZManaged[R, Nothing, A] - type Managed[+E, +A] = ZManaged[Any, E, A] - type UManaged[+A] = ZManaged[Any, Nothing, A] - type TaskManaged[+A] = ZManaged[Any, Throwable, A] + type IO[+E, +A] = ZIO[Any, E, A] // Succeed with an `A`, may fail with `E` , no requirements. + type Task[+A] = ZIO[Any, Throwable, A] // Succeed with an `A`, may fail with `Throwable`, no requirements. + type RIO[-R, +A] = ZIO[R, Throwable, A] // Succeed with an `A`, may fail with `Throwable`, requires an `R`. + type UIO[+A] = ZIO[Any, Nothing, A] // Succeed with an `A`, cannot fail , no requirements. + type URIO[-R, +A] = ZIO[R, Nothing, A] // Succeed with an `A`, cannot fail , requires an `R`. + + type Managed[+E, +A] = ZManaged[Any, E, A] //Manage an `A`, may fail with `E` , no requirements + type TaskManaged[+A] = ZManaged[Any, Throwable, A] //Manage an `A`, may fail with `Throwable`, no requirements + type RManaged[-R, +A] = ZManaged[R, Throwable, A] //Manage an `A`, may fail with `Throwable`, requires an `R` + type UManaged[+A] = ZManaged[Any, Nothing, A] //Manage an `A`, cannot fail , no requirements + type URManaged[-R, +A] = ZManaged[R, Nothing, A] //Manage an `A`, cannot fail , requires an `R` val Managed: ZManaged.type = ZManaged diff --git a/core/shared/src/main/scala/zio/random/package.scala b/core/shared/src/main/scala/zio/random/package.scala index 907d37afae5c..95e75cd42ff8 100644 --- a/core/shared/src/main/scala/zio/random/package.scala +++ b/core/shared/src/main/scala/zio/random/package.scala @@ -160,7 +160,7 @@ package object random { /** * Generates a pseudo-random boolean. */ - val nextBoolean: ZIO[Random, Nothing, Boolean] = + val nextBoolean: URIO[Random, Boolean] = ZIO.accessM(_.get.nextBoolean) /** @@ -173,88 +173,88 @@ package object random { * Generates a pseudo-random, uniformly distributed double between 0.0 and * 1.0. */ - val nextDouble: ZIO[Random, Nothing, Double] = ZIO.accessM(_.get.nextDouble) + val nextDouble: URIO[Random, Double] = ZIO.accessM(_.get.nextDouble) /** * Generates a pseudo-random double in the specified range. */ - def nextDoubleBetween(minInclusive: Double, maxExclusive: Double): ZIO[Random, Nothing, Double] = + def nextDoubleBetween(minInclusive: Double, maxExclusive: Double): URIO[Random, Double] = ZIO.accessM(_.get.nextDoubleBetween(minInclusive, maxExclusive)) /** * Generates a pseudo-random, uniformly distributed float between 0.0 and * 1.0. */ - val nextFloat: ZIO[Random, Nothing, Float] = + val nextFloat: URIO[Random, Float] = ZIO.accessM(_.get.nextFloat) /** * Generates a pseudo-random float in the specified range. */ - def nextFloatBetween(minInclusive: Float, maxExclusive: Float): ZIO[Random, Nothing, Float] = + def nextFloatBetween(minInclusive: Float, maxExclusive: Float): URIO[Random, Float] = ZIO.accessM(_.get.nextFloatBetween(minInclusive, maxExclusive)) /** * Generates a pseudo-random double from a normal distribution with mean 0.0 * and standard deviation 1.0. */ - val nextGaussian: ZIO[Random, Nothing, Double] = + val nextGaussian: URIO[Random, Double] = ZIO.accessM(_.get.nextGaussian) /** * Generates a pseudo-random integer. */ - val nextInt: ZIO[Random, Nothing, Int] = + val nextInt: URIO[Random, Int] = ZIO.accessM(_.get.nextInt) /** * Generates a pseudo-random integer in the specified range. */ - def nextIntBetween(minInclusive: Int, maxExclusive: Int): ZIO[Random, Nothing, Int] = + def nextIntBetween(minInclusive: Int, maxExclusive: Int): URIO[Random, Int] = ZIO.accessM(_.get.nextIntBetween(minInclusive, maxExclusive)) /** * Generates a pseudo-random integer between 0 (inclusive) and the specified * value (exclusive). */ - def nextIntBounded(n: => Int): ZIO[Random, Nothing, Int] = + def nextIntBounded(n: => Int): URIO[Random, Int] = ZIO.accessM(_.get.nextIntBounded(n)) /** * Generates a pseudo-random long. */ - val nextLong: ZIO[Random, Nothing, Long] = + val nextLong: URIO[Random, Long] = ZIO.accessM(_.get.nextLong) /** * Generates a pseudo-random long in the specified range. */ - def nextLongBetween(minInclusive: Long, maxExclusive: Long): ZIO[Random, Nothing, Long] = + def nextLongBetween(minInclusive: Long, maxExclusive: Long): URIO[Random, Long] = ZIO.accessM(_.get.nextLongBetween(minInclusive, maxExclusive)) /** * Generates a pseudo-random long between 0 (inclusive) and the specified * value (exclusive). */ - def nextLongBounded(n: => Long): ZIO[Random, Nothing, Long] = + def nextLongBounded(n: => Long): URIO[Random, Long] = ZIO.accessM(_.get.nextLongBounded(n)) /** * Generates a pseudo-random character from the ASCII range 33-126. */ - val nextPrintableChar: ZIO[Random, Nothing, Char] = + val nextPrintableChar: URIO[Random, Char] = ZIO.accessM(_.get.nextPrintableChar) /** * Generates a pseudo-random string of the specified length. */ - def nextString(length: => Int): ZIO[Random, Nothing, String] = + def nextString(length: => Int): URIO[Random, String] = ZIO.accessM(_.get.nextString(length)) /** * Sets the seed of this random number generator. */ - def setSeed(seed: Long): ZIO[Random, Nothing, Unit] = + def setSeed(seed: Long): URIO[Random, Unit] = ZIO.accessM(_.get.setSeed(seed)) /** diff --git a/core/shared/src/main/scala/zio/stm/STM.scala b/core/shared/src/main/scala/zio/stm/STM.scala index bc49cffcd63f..327a1c45b686 100644 --- a/core/shared/src/main/scala/zio/stm/STM.scala +++ b/core/shared/src/main/scala/zio/stm/STM.scala @@ -186,7 +186,7 @@ object STM { * @see See [[zio.stm.ZSTM.ifM]] */ def ifM[E](b: STM[E, Boolean]): ZSTM.IfM[Any, E] = - new ZSTM.IfM(b) + ZSTM.ifM(b) /** * @see See [[zio.stm.ZSTM.iterate]] @@ -324,8 +324,8 @@ object STM { /** * @see See [[zio.stm.ZSTM.unlessM]] */ - def unlessM[E](b: STM[E, Boolean])(stm: => STM[E, Any]): STM[E, Unit] = - ZSTM.unlessM(b)(stm) + def unlessM[E](b: STM[E, Boolean]): ZSTM.UnlessM[Any, E] = + ZSTM.unlessM(b) /** * @see See [[zio.stm.ZSTM.validate]] @@ -363,7 +363,8 @@ object STM { /** * @see See [[zio.stm.ZSTM.whenM]] */ - def whenM[E](b: STM[E, Boolean])(stm: => STM[E, Any]): STM[E, Unit] = ZSTM.whenM(b)(stm) + def whenM[E](b: STM[E, Boolean]): ZSTM.WhenM[Any, E] = + ZSTM.whenM(b) private[zio] def succeedNow[A](a: A): USTM[A] = ZSTM.succeedNow(a) diff --git a/core/shared/src/main/scala/zio/stm/ZSTM.scala b/core/shared/src/main/scala/zio/stm/ZSTM.scala index 97ac2812aadd..c7677e5453a1 100644 --- a/core/shared/src/main/scala/zio/stm/ZSTM.scala +++ b/core/shared/src/main/scala/zio/stm/ZSTM.scala @@ -1436,8 +1436,8 @@ object ZSTM { /** * The moral equivalent of `if (!p) exp` when `p` has side-effects */ - def unlessM[R, E](b: ZSTM[R, E, Boolean])(stm: => ZSTM[R, E, Any]): ZSTM[R, E, Unit] = - b.flatMap(b => if (b) unit else stm.unit) + def unlessM[R, E](b: ZSTM[R, E, Boolean]): ZSTM.UnlessM[R, E] = + new ZSTM.UnlessM(b) /** * Feeds elements of type `A` to `f` and accumulates all errors in error @@ -1484,8 +1484,8 @@ object ZSTM { /** * The moral equivalent of `if (p) exp` when `p` has side-effects */ - def whenM[R, E](b: ZSTM[R, E, Boolean])(stm: => ZSTM[R, E, Any]): ZSTM[R, E, Unit] = - b.flatMap(b => if (b) stm.unit else unit) + def whenM[R, E](b: ZSTM[R, E, Boolean]): ZSTM.WhenM[R, E] = + new ZSTM.WhenM(b) private[zio] def succeedNow[A](a: A): USTM[A] = succeed(a) @@ -1505,6 +1505,16 @@ object ZSTM { b.flatMap(b => if (b) onTrue else onFalse) } + final class UnlessM[R, E](private val b: ZSTM[R, E, Boolean]) { + def apply[R1 <: R, E1 >: E](stm: => ZSTM[R1, E1, Any]): ZSTM[R1, E1, Unit] = + b.flatMap(b => if (b) unit else stm.unit) + } + + final class WhenM[R, E](private val b: ZSTM[R, E, Boolean]) { + def apply[R1 <: R, E1 >: E](stm: => ZSTM[R1, E1, Any]): ZSTM[R1, E1, Unit] = + b.flatMap(b => if (b) stm.unit else unit) + } + private final class Resumable[E, E1, A, B]( val stm: STM[E, A], val ks: Stack[internal.TExit[E, A] => STM[E1, B]] diff --git a/core/shared/src/main/scala/zio/system/package.scala b/core/shared/src/main/scala/zio/system/package.scala index d062f6ec470b..a2d48d6a10a2 100644 --- a/core/shared/src/main/scala/zio/system/package.scala +++ b/core/shared/src/main/scala/zio/system/package.scala @@ -149,7 +149,7 @@ package object system { * Retrieves the value of a system property or else return the specified * fallback value. **/ - def propertyOrElse(prop: String, alt: => String): ZIO[System, Throwable, String] = + def propertyOrElse(prop: String, alt: => String): RIO[System, String] = ZIO.accessM(_.get.propertyOrElse(prop, alt)) /** @@ -162,6 +162,6 @@ package object system { /** * Retrieves the value of the system-specific line separator. **/ - val lineSeparator: ZIO[System, Nothing, String] = + val lineSeparator: URIO[System, String] = ZIO.accessM(_.get.lineSeparator) } diff --git a/docs/about/coding_guidelines.md b/docs/about/coding_guidelines.md new file mode 100644 index 000000000000..248421eaee57 --- /dev/null +++ b/docs/about/coding_guidelines.md @@ -0,0 +1,149 @@ +--- +id: about_coding_guidelines +title: "ZIO Coding Guidelines" +--- + +These are coding guidelines strictly for ZIO contributors for ZIO projects and +not general conventions to be applied by the Scala community at large. + +Additionally, bear in mind that, although we try to enforce these rules to the +best of our ability, both via automated rules (scalafix) and strict reviewing +processes, it is both possible to find existing code that does not comply to +these rules. If that is the case, we would be extremely grateful if you could +make a contribution, by proving a fix to said issue. + +Last, but not least, these rules are continuously evolving and as such, +refer to them once in a while when in doubt. + +### Defining classes and traits + +1. Value classes must be final and extend `AnyVal`. +This is done to avoid allocating runtime objects; + +2. Method extension classes must be final and extend `AnyVal`; + +3. Avoid overloading standard interfaces. When creating services avoid using the same names as well known standard interfaces. +Example: Instead of having a service `Random` with methods `nextLong(n)` and `nextInt(n)` consider choosing something like +`nextLongBounded(n)` and `nextIntBounded(n)`. + +4. Sealed traits that are ADTs (Algebraic data types) should extend `Product` and `Serializable`. +This is done to help the compiler infer types; + +5. Regular traits and sealed trait that do not form ADTs should extend `Serializable` but not `Product`; + +6. Traits should always extend `Serializable`. (i.e. `ZIO`). + +### Final and private modifiers + +1. All methods on classes / traits are declared `final`, by default; + +2. No methods on objects declared `final`, because they are `final` by default; + +3. No methods on final classes declared `final`, because they are `final` by default; + +4. All classes inside objects should be defined `final`, because otherwise they could still be extended; + +5. In general, classes that are not case classes have their constructors & constructor parameters private. + Typically it is not good practice to expose constructors and constructor parameters but exceptions apply (i.e. `Assertion` and `TestAnnotation`); + +6. All `vals` declared `final`, even in objects or `final classes`, if they are constant expressions and without type annotations; + +7. Package-private `vals` and methods should be declared `final`. + +### Refactoring + +1. If a class has all its members `final`, the class should be declared `final` and `final` member annotations should be removed except constant expressions; + +2. All type annotations should use the least powerful type alias. This means, that, let us say, a `ZIO` effect that has + no dependencies but throws an arbitrary error, should be defined as `IO`. + +### Understanding naming of parameters or values + +ZIO code often uses the following naming conventions and you might be asked to change method parameters to follow this conventions. This guide can help you understand where the names come from. +Naming expectations can be helpful in understanding the role of certain parameters without even glancing at its type signature when reading code or class/method signatures. + +1. Partial functions have a shortened name `pf`; + +2. In ZIO implicit parameters are often used as compiler evidences; + These evidences help you, as a developer, prove something to the compiler (at compile time) and they have the ability to add constraints to a method; + They are typically called `ev` if there is only one. Or `ev1`, `ev2`... if more than one; + +3. Promises are called `p` (unless in its own class methods, in that case it is called `that`, like point 6 defines); + +4. Functions are called `fn`, `fn1`, unless they bear specific meaning: `use`, `release`; + +4. ZIO effects are called `f`, unless they bear specific meaning like partially providing environment: `r0`; + +4. Consider methods ending with _ having more meaningful names; + +5. Iterable are called `in`; + +6. When a parameter type equals own (in a method of a trait) call it `that`; + +7. Be mindful of using by-name parameters. Mind the `Function[0]` extra allocation and loss of clean syntax when invoking the method. + Loss of syntax means that instead of being able to do something like `f.flatMap(ZIO.success)` you require to explicitly do `f.flatMap(ZIO.success(_))`; + +8. Fold or fold variants initial values are called `zero`. + +### Understanding naming of methods + +ZIO goes to great lengths to define method names that are intuitive to the library user. Naming is hard!!! +This section will attempt to provide some guidelines and examples to document, guide and explain naming of methods in ZIO. + +1. Methods that lift pure values to effects are dangerous. Dangerous in the sense that they can potentially have dangerous side-effects. + Such methods should have a default lazy variant and an eager variant for advanced users that are aware they absolutely do not have side-effects in their code, + having slight gains in performance. The lazy variant should have a normal name (succeed, fail, die, lift) and the eager variant should have a `now` suffix + (succeedNow, failNow, dieNow, liftNow) which makes it clear of its eager behaviour; + +2. Methods that have the form of `List#zip` are called `zip`, and have an alias called `<*>`. The parallel version, if applicable, has the name `zipPar`, with an alias called `<&>`; + +3. Methods that are intended to capture side-effects, convert them into functional effects, should be prefixed by effect*. For example, `ZIO.effect`; + +4. The dual of zip, which is trying either a left or right side, producing an Either of the result, should be called `orElseEither`, with alias `<+>`. + The simplified variant where both left and right have the same type should be called `orElse`, with alias `<>`; + +5. Constructors for a data type `X` that are based on another data type `Y` should be placed in the companion object `X` and named `fromY`. + For example, `ZIO.fromOption`, `ZStream.fromEffect`; + +6. Parallel versions of methods should be named the same, but with a `Par` suffix. Parallel versions with a bound on parallelism should use a `ParN` suffix; + +7. `Foreach` should be used as the default traverse operation, with `traverse` retained as an alias for programmers with an FP background. For example, `ZIO.foreach`. + +### Type annotations + +ZIO goes to great lengths to take advantage of the scala compiler in varied ways. Type variance is one of them. +The following rules are good to have in mind when adding new `types`, `traits` or `classes` that have either covariant or contravariant types. + +1. Generalized ADTs should always have type annotation. (i.e. `final case class Fail[+E](value: E) extends Cause[E]`); + +2. Type alias should always have type annotation. Much like in Generalized ADTs defining type aliases should have carry the type annotations + (i.e. `type IO[+E, +A] = ZIO[Any, E, A]`). + + +### Method alphabetization + +In general the following rules should be applied regarding method alphabetization. +To fix forward references of values we recommend the programmer to make them lazy (`lazy val`). +Operators are any methods that only have non-letter characters (i.e. `<*>` , `<>`, `*>`). + +1. Public abstract defs / vals listed first, and alphabetized, with operators appearing before names. + +2. Public concrete defs / vals listed second, and alphabetized, with operators appearing before names. + +3. Private implementation details listed third, and alphabetized, with operators appearing before names. + +### Scala documentation + +It is strongly recommended to use scala doc links when referring to other members. +This both makes it easier for users to navigate the documentation and enforces that the references are accurate. +A good example of this are `ZIO` type aliases that are extremely pervasive in the codebase: `UIO`, `Task`, `RIO`, `URIO` and `UIO`. +To make it easy for developers to see the implementation scala doc links are used, for example: + +``` + /** + * @see See [[zio.ZIO.absolve]] + */ + def absolve[R, A](v: RIO[R, Either[Throwable, A]]): RIO[R, A] = + ZIO.absolve(v) +``` + diff --git a/docs/about/contributing.md b/docs/about/contributing.md index f6c01270516a..8f0c2e5227c5 100644 --- a/docs/about/contributing.md +++ b/docs/about/contributing.md @@ -17,7 +17,7 @@ To begin contributing, please follow these steps: ### Get The Project -If you don't already have one, sign up for a free [GitHub Account](https://github.com/join?source=header-home). +If you do not already have one, sign up for a free [GitHub Account](https://github.com/join?source=header-home). After you [log into](https://github.com/login) GitHub using your account, go to the [ZIO Project Page](https://github.com/zio/zio), and click on [Fork](https://github.com/zio/zio/fork) to fork the ZIO repository into your own account. diff --git a/docs/datatypes/fiber.md b/docs/datatypes/fiber.md index e88a1939540e..05d2e9a59769 100644 --- a/docs/datatypes/fiber.md +++ b/docs/datatypes/fiber.md @@ -61,7 +61,7 @@ The `IO` error model is simple, consistent, permits both typed errors and termin An `IO[E, A]` value may only raise errors of type `E`. These errors are recoverable by using the `either` method. The resulting effect cannot fail, because the failure case bas been exposed as part of the `Either` success case. ```scala mdoc:silent -val error: ZIO[Any, Throwable, String] = IO.fail(new RuntimeException("Some Error")) +val error: Task[String] = IO.fail(new RuntimeException("Some Error")) val errorEither: ZIO[Any, Nothing, Either[Throwable, String]] = error.either ``` diff --git a/docs/datatypes/managed.md b/docs/datatypes/managed.md index 55d85a2db7f8..fd448cdb7447 100644 --- a/docs/datatypes/managed.md +++ b/docs/datatypes/managed.md @@ -46,7 +46,7 @@ import zio._ import zio.console._ val zManagedResource: ZManaged[Console, Nothing, Unit] = ZManaged.make(console.putStrLn("acquiring"))(_ => console.putStrLn("releasing")) -val zUsedResource: ZIO[Console, Nothing, Unit] = zManagedResource.use { _ => console.putStrLn("running") } +val zUsedResource: URIO[Console, Unit] = zManagedResource.use { _ => console.putStrLn("running") } ``` ## Combining Managed diff --git a/docs/datatypes/sink.md b/docs/datatypes/sink.md index a39d8dd47298..fea53887a41e 100644 --- a/docs/datatypes/sink.md +++ b/docs/datatypes/sink.md @@ -25,7 +25,7 @@ stream.run(sink) The `zio.stream` provides numerous kinds of sinks to use. -Collecting all elements into `List[A]`: +Collecting all elements into `Chunk[A]`: ```scala mdoc:silent ZSink.collectAll[Int] @@ -80,5 +80,5 @@ Sink.collectAll[String].contramap[Int](_.toString + "id") A `dimap` is an extended `contramap` that additionally transforms sink's output: ```scala mdoc:silent -Sink.collectAll[String].dimap[Int, List[String]](_.toString + "id", _.take(10)) +Sink.collectAll[String].dimap[Int, Chunk[String]](_.toString + "id", _.take(10)) ``` diff --git a/docs/datatypes/zlayer.md b/docs/datatypes/zlayer.md index 2bd93f7bbded..408e98767f7c 100644 --- a/docs/datatypes/zlayer.md +++ b/docs/datatypes/zlayer.md @@ -21,7 +21,7 @@ import zio._ object Example extends zio.App { - def run(args: List[String]): ZIO[ZEnv, Nothing, ExitCode] = + def run(args: List[String]): URIO[ZEnv, ExitCode] = zio.provideLayer(nameLayer).as(ExitCode.success) val zio = for { @@ -59,7 +59,7 @@ object moduleA { } } - def letsGoA(v: Int): ZIO[ModuleA, Nothing, String] = + def letsGoA(v: Int): URIO[ModuleA, String] = ZIO.accessM(_.get.letsGoA(v)) } @@ -84,7 +84,7 @@ object moduleB { } } - def letsGoB(v: Int): ZIO[ModuleB, Nothing, String] = + def letsGoB(v: Int): URIO[ModuleB, String] = ZIO.accessM(_.get.letsGoB(v)) } @@ -164,7 +164,7 @@ object ZLayerApp1 extends scala.App { } } - val foo: ZIO[ModuleC, Nothing, Int] = + val foo: URIO[ModuleC, Int] = ZIO.accessM(_.get.foo) } diff --git a/docs/ecosystem/ecosystem.md b/docs/ecosystem/ecosystem.md index c8cf972bb604..f601cb57dc49 100644 --- a/docs/ecosystem/ecosystem.md +++ b/docs/ecosystem/ecosystem.md @@ -3,6 +3,8 @@ id: ecosystem title: "Ecosystem" --- +### If you find a new great library, talk, resource, slides or project, related to ZIO, consider adding to the list with your PR + ## Official ZIO Libraries These libraries are hosted in the [ZIO organization](https://github.com/zio/) on Github, and are generally maintained by core contributors to ZIO. @@ -25,7 +27,7 @@ If you know a useful library that has direct support for ZIO, please consider [s - [cakeless](https://github.com/itkpi/cakeless): Wire your cakes automatically into zio environment - [caliban](https://github.com/ghostdogpr/caliban): Functional GraphQL backend in Scala -- [d4s](https://github.com/PlayQ/d4s): "Dynamo DB Database done Scala way". A library that allows accessing the DynamoDB in a purely functional way. +- [d4s](https://github.com/PlayQ/d4s): "Dynamo DB Database done Scala way". A library that allows accessing the DynamoDB in a purely functional way - [distage](https://github.com/7mind/izumi): Staged, transparent and debuggable runtime & compile-time Dependency Injection Framework - [elastic4s](https://github.com/sksamuel/elastic4s): Elasticsearch Scala Client - Reactive, Non Blocking, Type Safe, HTTP Client - [idealingua](https://github.com/7mind/izumi): API Definition, Data Modeling and RPC Language, optimized for fast prototyping – like gRPC, but with a human face @@ -40,12 +42,19 @@ If you know a useful library that has direct support for ZIO, please consider [s - [zio-easymock](https://github.com/egast/zio-easymock): EasyMock mocking for zio-test - [zio-email](https://github.com/funcit/zio-email): Purely functional email client - [zio-event-sourcing](https://github.com/holinov/zio-event-sourcing): Purely functional concurent and scalable persistance layer +- [zio-grpc](https://github.com/scalapb/zio-grpc): A native gRPC support for ZIO - [zio-interop-log4j2](https://github.com/mlangc/zio-interop-log4j2): Fiber aware MDC logging for Log4j 2 - [zio-kinesis](https://github.com/svroonland/zio-kinesis): ZIO Streams based AWS Kinesis client - [zio-saga](https://github.com/VladKopanev/zio-saga): Purely functional transaction management with Saga pattern - [zio-slf4j](https://github.com/NeQuissimus/zio-slf4j): Referentially transparent logging with slf4j - [zio-slick](https://github.com/rleibman/zio-slick): Bridge library between ZIO and Slick Functional Relational Mapping Library + +## Tools for ZIO + +- [ZIO IntelliJ](https://github.com/zio/zio-intellij): A complimentary, community-developed plugin for IntelliJ IDEA, brings enhancements when using ZIO in your projects + + ## ZIO Interoperability Libraries ZIO provides the ability to interoperate with other parts of the broader ecosystem, see [Interop](../interop/index.md) for overview. diff --git a/docs/howto/howto_macros.md b/docs/howto/howto_macros.md index 09c554668404..08e406723c1c 100644 --- a/docs/howto/howto_macros.md +++ b/docs/howto/howto_macros.md @@ -44,7 +44,7 @@ import zio.macros.accessible @accessible object AccountObserver { trait Service { - def processEvent(event: AccountEvent): ZIO[Any, Nothing, Unit] + def processEvent(event: AccountEvent): UIO[Unit] } // below will be autogenerated diff --git a/docs/howto/mock_services.md b/docs/howto/mock_services.md index 2c8a0f0730a0..b863f8c467e3 100644 --- a/docs/howto/mock_services.md +++ b/docs/howto/mock_services.md @@ -48,7 +48,7 @@ trait Event import zio._ import zio.console.Console -def processEvent(event: Event): ZIO[Console, Nothing, Unit] = +def processEvent(event: Event): URIO[Console, Unit] = console.putStrLn(s"Got $event") ``` @@ -61,7 +61,7 @@ With ZIO, we've regained to ability to reason about the effects called. We know However, the same method could be implemented as: ```scala mdoc:silent -def processEvent2(event: Event): ZIO[Console, Nothing, Unit] = +def processEvent2(event: Event): URIO[Console, Unit] = ZIO.unit ``` @@ -200,8 +200,8 @@ type AccountObserver = Has[AccountObserver.Service] object AccountObserver { trait Service { - def processEvent(event: AccountEvent): ZIO[Any, Nothing, Unit] - def runCommand(): ZIO[Any, Nothing, Unit] + def processEvent(event: AccountEvent): UIO[Unit] + def runCommand(): UIO[Unit] } def processEvent(event: AccountEvent) = @@ -397,10 +397,10 @@ type PolyExample = Has[PolyExample.Service] object PolyExample { trait Service { - def polyInput[I: Tag](input: I): ZIO[Any, Throwable, String] - def polyError[E: Tag](input: Int): ZIO[Any, E, String] - def polyOutput[A: Tag](input: Int): ZIO[Any, Throwable, A] - def polyAll[I: Tag, E: Tag, A: Tag](input: I): ZIO[Any, E, A] + def polyInput[I: Tag](input: I): Task[String] + def polyError[E: Tag](input: Int): IO[E, String] + def polyOutput[A: Tag](input: Int): Task[A] + def polyAll[I: Tag, E: Tag, A: Tag](input: I): IO[E, A] } } ``` diff --git a/docs/howto/use_modules_and_layers.md b/docs/howto/use_modules_and_layers.md index ca4157d32c95..41f87f94841f 100644 --- a/docs/howto/use_modules_and_layers.md +++ b/docs/howto/use_modules_and_layers.md @@ -15,7 +15,7 @@ To access the DB we need a `DBConnection`, and each step in our program represen The result is a program that, in turn, depends on the `DBConnection`. ```scala mdoc:invisible -import zio.{ Has, IO, Layer, UIO, ZEnv, ZIO, ZLayer } +import zio.{ Has, IO, Layer, UIO, URIO, ZEnv, ZIO, ZLayer } import zio.clock.Clock import zio.console.Console import zio.random.Random @@ -33,10 +33,10 @@ case class User(id: UserId, name: String) ```scala mdoc:silent def getUser(userId: UserId): ZIO[DBConnection, Nothing, Option[User]] = UIO(???) -def createUser(user: User): ZIO[DBConnection, Nothing, Unit] = UIO(???) +def createUser(user: User): URIO[DBConnection, Unit] = UIO(???) val user: User = User(UserId(1234), "Chet") -val created: ZIO[DBConnection, Nothing, Boolean] = for { +val created: URIO[DBConnection, Boolean] = for { maybeUser <- getUser(user.id) res <- maybeUser.fold(createUser(user).as(true))(_ => ZIO.succeed(false)) } yield res @@ -46,7 +46,7 @@ To run the program we must supply a `DBConnection` through `provide`, before fee ```scala val dbConnection: DBConnection = ??? -val runnable: ZIO[Any, Nothing, Boolean] = created.provide(dbConnection) +val runnable: UIO[Boolean] = created.provide(dbConnection) val finallyCreated = runtime.unsafeRun(runnable) ``` @@ -90,7 +90,7 @@ object UserRepo { ``` ```scala mdoc:reset:invisible -import zio.{ Has, IO, Layer, UIO, ZEnv, ZIO, ZLayer } +import zio.{ Has, IO, Layer, UIO, URIO, ZEnv, ZIO, ZLayer } import zio.clock.Clock import zio.console.Console import zio.random.Random @@ -211,10 +211,10 @@ object Logging { ) //accessor methods - def info(s: String): ZIO[Logging, Nothing, Unit] = + def info(s: String): URIO[Logging, Unit] = ZIO.accessM(_.get.info(s)) - def error(s: String): ZIO[Logging, Nothing, Unit] = + def error(s: String): URIO[Logging, Unit] = ZIO.accessM(_.get.error(s)) } ``` @@ -224,9 +224,9 @@ The accessor methods are provided so that we can build programs without botherin ```scala mdoc:silent val user2: User = User(UserId(123), "Tommy") val makeUser: ZIO[Logging with UserRepo, DBError, Unit] = for { - _ <- Logging.info(s"inserting user") // ZIO[Logging, Nothing, Unit] + _ <- Logging.info(s"inserting user") // URIO[Logging, Unit] _ <- UserRepo.createUser(user2) // ZIO[UserRepo, DBError, Unit] - _ <- Logging.info(s"user inserted") // ZIO[Logging, Nothing, Unit] + _ <- Logging.info(s"user inserted") // URIO[Logging, Unit] } yield () ``` diff --git a/docs/overview/creating_effects.md b/docs/overview/creating_effects.md index 970b28b6d306..222fea3c05fb 100644 --- a/docs/overview/creating_effects.md +++ b/docs/overview/creating_effects.md @@ -6,7 +6,7 @@ title: "Creating Effects" This section explores some of the common ways to create ZIO effects from values, from common Scala types, and from both synchronous and asynchronous side-effects. ```scala mdoc:invisible -import zio.{ ZIO, Task, UIO, IO } +import zio.{ ZIO, Task, UIO, URIO, IO } ``` ## From Success Values @@ -58,13 +58,13 @@ Scala's standard library contains a number of data types that can be converted i An `Option` can be converted into a ZIO effect using `ZIO.fromOption`: ```scala mdoc:silent -val zoption: ZIO[Any, Unit, Int] = ZIO.fromOption(Some(2)) +val zoption: IO[Unit, Int] = ZIO.fromOption(Some(2)) ``` The error type of the resulting effect is `Unit`, because the `None` case of `Option` provides no information on why the value is not there. You can change the `Unit` into a more specific error type using `ZIO#mapError`: ```scala mdoc:silent -val zoption2: ZIO[Any, String, Int] = zoption.mapError(_ => "It wasn't there!") +val zoption2: IO[String, Int] = zoption.mapError(_ => "It wasn't there!") ``` ### Either @@ -94,7 +94,7 @@ The error type of the resulting effect will always be `Throwable`, because `Try` A function `A => B` can be converted into a ZIO effect with `ZIO.fromFunction`: ```scala mdoc:silent -val zfun: ZIO[Int, Nothing, Int] = +val zfun: URIO[Int, Int] = ZIO.fromFunction((i: Int) => i * i) ``` diff --git a/docs/overview/testing_effects.md b/docs/overview/testing_effects.md index 8fca4dff4bd8..1dcdc35c7a4b 100644 --- a/docs/overview/testing_effects.md +++ b/docs/overview/testing_effects.md @@ -32,7 +32,7 @@ When the environment is a type with fields, then the `ZIO.access` method can be ```scala mdoc:silent:nest final case class Config(server: String, port: Int) -val configString: ZIO[Config, Nothing, String] = +val configString: URIO[Config, String] = for { server <- ZIO.access[Config](_.server) port <- ZIO.access[Config](_.port) @@ -65,7 +65,7 @@ Effects that require an environment cannot be run without first _providing_ thei The simplest way to provide an effect the environment that it requires is to use the `ZIO#provide` method: ```scala mdoc:silent -val square: ZIO[Int, Nothing, Int] = +val square: URIO[Int, Int] = for { env <- ZIO.environment[Int] } yield env * env @@ -115,10 +115,10 @@ In order to make it easier to access the database service as an environmental ef ```scala mdoc:silent object db { - def lookup(id: UserID): ZIO[Database, Throwable, UserProfile] = + def lookup(id: UserID): RIO[Database, UserProfile] = ZIO.accessM(_.database.lookup(id)) - def update(id: UserID, profile: UserProfile): ZIO[Database, Throwable, Unit] = + def update(id: UserID, profile: UserProfile): RIO[Database, Unit] = ZIO.accessM(_.database.update(id, profile)) } ``` @@ -130,7 +130,7 @@ While these helpers are not required, because we can access the database module Now that we have defined a module and helper functions, we are now ready to build an example that uses the database service: ```scala mdoc:silent -val lookedupProfile: ZIO[Database, Throwable, UserProfile] = +val lookedupProfile: RIO[Database, UserProfile] = for { profile <- db.lookup(userId) } yield profile @@ -164,9 +164,9 @@ We now have a database module, helpers to interact with the database module, and We can now provide the live database module to our application, using `ZIO.provide`: ```scala mdoc:silent -def main: ZIO[Database, Throwable, Unit] = ??? +def main: RIO[Database, Unit] = ??? -def main2: ZIO[Any, Throwable, Unit] = +def main2: Task[Unit] = main.provide(DatabaseLive) ``` @@ -207,9 +207,9 @@ Because this module will only be used in tests, it simulates interaction with a To test code that requires the database, we need only provide it with our test database module: ```scala mdoc:silent -def code: ZIO[Database, Throwable, Unit] = ??? +def code: RIO[Database, Unit] = ??? -def code2: ZIO[Any, Throwable, Unit] = +def code2: Task[Unit] = code.provide(TestDatabase) ``` diff --git a/docs/resources/resources.md b/docs/resources/resources.md index d0f4803ce3be..008949c6d37e 100644 --- a/docs/resources/resources.md +++ b/docs/resources/resources.md @@ -3,10 +3,13 @@ id: resources title: "Resources" --- +### If you find a new great library, talk, resource, slides or project, related to ZIO, consider adding to the list with your PR + ## Blog Articles _These articles reflect the state of ZIO at the time of their publication. The code samples might be outdated, considering ZIO was early in development at the time they were written. However, the concepts are still relevant._ +- [Building a cool CLI with Decline for my ZIO App](https://medium.com/@pascal.mengelt/building-a-cool-cli-with-decline-for-my-zio-app-80e095b2899a) by Pascal Mengelt (May 2020) - [uzhttp + sttp for light-weight http and websockets](https://timpigden.github.io/_pages/zio-uzhttp-sttp/uzhttp-sttp.html) by Tim Pigden (April 2020) - [Effective testing with ZIO Test (RC18)](https://scala.monster/zio-test/) by Pavels Sisojevs (April 2020) - [ZIO with http4s, Auth, Codecs and zio-tests (RC18)](https://timpigden.github.io/_pages/zio-http4s/intro.html) by Tim Pigden (April 2020) @@ -61,6 +64,9 @@ _These articles reflect the state of ZIO at the time of their publication. The c - [ZIO: Next-Generation Effects in Scala](https://www.youtube.com/watch?v=mkSHhsJXjdc&t=6s) by John De Goes (October 2018) - [ZIO Queue: A new Queue for a new Era](https://www.youtube.com/watch?v=8JLprl34xEw&t=2437s) by John De Goes (September 2018) +## Cookbooks +- [ZIO Cookbook](https://github.com/Neurodyne/zio-cookbook) A beginners' tour to ZIO by Boris V.Kuznetsov + ## Cheat Sheet - [ZIO Cheat Sheet](https://github.com/ghostdogpr/zio-cheatsheet) diff --git a/docs/usecases/testing.md b/docs/usecases/testing.md index d29855124d0e..a3eee4f0e1cf 100644 --- a/docs/usecases/testing.md +++ b/docs/usecases/testing.md @@ -26,7 +26,7 @@ import zio.test.environment._ import HelloWorld._ object HelloWorld { - def sayHello: ZIO[Console, Nothing, Unit] = + def sayHello: URIO[Console, Unit] = console.putStrLn("Hello, World!") } diff --git a/examples/shared/src/main/scala-2.x/zio/examples/macros/AccessibleMacroExample.scala b/examples/shared/src/main/scala-2.x/zio/examples/macros/AccessibleMacroExample.scala index 4dadb40396b9..f553ac250c60 100644 --- a/examples/shared/src/main/scala-2.x/zio/examples/macros/AccessibleMacroExample.scala +++ b/examples/shared/src/main/scala-2.x/zio/examples/macros/AccessibleMacroExample.scala @@ -1,6 +1,6 @@ package zio.examples.macros -import zio.{ random, Has, IO, UIO, URIO, ZIO, ZLayer } +import zio.{ random, Chunk, Has, IO, RIO, UIO, URIO, ZIO, ZLayer } import zio.console.Console import zio.stream.{ ZSink, ZStream } import zio.random.Random @@ -26,25 +26,27 @@ object AccessibleMacroExample { val value: String def function(n: Int): String def stream(n: Int): ZStream[Any, String, Int] - def sink(n: Int): ZSink[Any, Nothing, Int, List[Int]] + def sink(n: Int): ZSink[Any, Nothing, Int, Chunk[Int]] } val live: ZLayer[Console, Nothing, Has[Service]] = - ZLayer.fromService(console => new Service { - val foo: UIO[Unit] = UIO.unit - def bar(n: Int): UIO[Unit] = console.putStrLn(s"bar $n") - def baz(x: Int, y: Int): IO[String, Int] = UIO.succeed(x + y) - def poly[A](a: A): IO[Long, A] = UIO.succeed(a) - def poly2[A <: Foo](a: Wrapped[A]): IO[String, List[A]] = UIO.succeed(List(a.value)) - def dependent(n: Int): ZIO[Random, Long, Int] = random.nextIntBounded(n) - val value: String = "foo" - def function(n: Int): String = s"foo $n" - def stream(n: Int): ZStream[Any, String, Int] = ZStream.fromIterable(List(1, 2, 3)) - def sink(n: Int): ZSink[Any, Nothing, Int, List[Int]] = ZSink.collectAll - }) + ZLayer.fromService(console => + new Service { + val foo: UIO[Unit] = UIO.unit + def bar(n: Int): UIO[Unit] = console.putStrLn(s"bar $n") + def baz(x: Int, y: Int): IO[String, Int] = UIO.succeed(x + y) + def poly[A](a: A): IO[Long, A] = UIO.succeed(a) + def poly2[A <: Foo](a: Wrapped[A]): IO[String, List[A]] = UIO.succeed(List(a.value)) + def dependent(n: Int): ZIO[Random, Long, Int] = random.nextIntBounded(n) + val value: String = "foo" + def function(n: Int): String = s"foo $n" + def stream(n: Int): ZStream[Any, String, Int] = ZStream.fromIterable(List(1, 2, 3)) + def sink(n: Int): ZSink[Any, Nothing, Int, Chunk[Int]] = ZSink.collectAll + } + ) // can use accessors even in the same compilation unit - val program: URIO[AccessibleMacroExample with Random, (Int, String, Long, List[Foo], Int, String, String, ZStream[Any, String, Int], ZSink[Any, Nothing, Int, List[Int]])] = + val program: URIO[AccessibleMacroExample with Random, (Int, String, Long, List[Foo], Int, String, String, ZStream[Any, String, Int], ZSink[Any, Nothing, Int, Chunk[Int]])] = for { _ <- AccessibleMacroExample.foo _ <- AccessibleMacroExample.bar(1) @@ -60,16 +62,16 @@ object AccessibleMacroExample { } yield (v1, v2, v3, v4, v5, v6, v7, v8, v9) // sanity check - val _foo : ZIO[AccessibleMacroExample, Nothing, Unit] = AccessibleMacroExample.foo - def _bar(n: Int) : ZIO[AccessibleMacroExample, Nothing, Unit] = AccessibleMacroExample.bar(n) - def _baz(x: Int, y: Int) : ZIO[AccessibleMacroExample, String, Int] = AccessibleMacroExample.baz(x, y) - def _poly[A](a: A) : ZIO[AccessibleMacroExample, Long, A] = AccessibleMacroExample.poly(a) - def _poly2[A <: Foo](a: Wrapped[A]) : ZIO[AccessibleMacroExample, String, List[A]] = AccessibleMacroExample.poly2(a) - def _dependent(n: Int) : ZIO[AccessibleMacroExample with Random, Long, Int] = AccessibleMacroExample.dependent(n) - def _value : ZIO[AccessibleMacroExample, Throwable, String] = AccessibleMacroExample.value - def _function(n: Int) : ZIO[AccessibleMacroExample, Throwable, String] = AccessibleMacroExample.function(n) - def _stream(n: Int) : ZIO[AccessibleMacroExample, Nothing, ZStream[Any, String, Int]] = AccessibleMacroExample.stream(n) - def _sink(n: Int) : ZIO[AccessibleMacroExample, Nothing, ZSink[Any, Nothing, Int, List[Int]]] = AccessibleMacroExample.sink(n) + val _foo : URIO[AccessibleMacroExample, Unit] = AccessibleMacroExample.foo + def _bar(n: Int) : URIO[AccessibleMacroExample, Unit] = AccessibleMacroExample.bar(n) + def _baz(x: Int, y: Int) : ZIO[AccessibleMacroExample, String, Int] = AccessibleMacroExample.baz(x, y) + def _poly[A](a: A) : ZIO[AccessibleMacroExample, Long, A] = AccessibleMacroExample.poly(a) + def _poly2[A <: Foo](a: Wrapped[A]) : ZIO[AccessibleMacroExample, String, List[A]] = AccessibleMacroExample.poly2(a) + def _dependent(n: Int) : ZIO[AccessibleMacroExample with Random, Long, Int] = AccessibleMacroExample.dependent(n) + def _value : RIO[AccessibleMacroExample, String] = AccessibleMacroExample.value + def _function(n: Int) : RIO[AccessibleMacroExample, String] = AccessibleMacroExample.function(n) + def _stream(n: Int) : ZIO[AccessibleMacroExample, Nothing, ZStream[Any, String, Int]] = AccessibleMacroExample.stream(n) + def _sink(n: Int) : ZIO[AccessibleMacroExample, Nothing, ZSink[Any, Nothing, Int, Chunk[Int]]] = AccessibleMacroExample.sink(n) // macro autogenerates accessors for `foo`, `bar`, `baz`, `poly`, `poly2`, `value` and `function` below } diff --git a/examples/shared/src/main/scala-2.x/zio/examples/test/MockableMacroExample.scala b/examples/shared/src/main/scala-2.x/zio/examples/test/MockableMacroExample.scala index 3d30a58d8859..ed8a2829fa88 100644 --- a/examples/shared/src/main/scala-2.x/zio/examples/test/MockableMacroExample.scala +++ b/examples/shared/src/main/scala-2.x/zio/examples/test/MockableMacroExample.scala @@ -1,8 +1,7 @@ package zio.examples.test import zio.test.mock.mockable -import zio.{ Tag, UIO } -import zio.{ IO, Tag, Task, UIO, URIO, ZIO } +import zio.{ IO, Tag, Task, UIO, URIO } object DiffrentScopeExample { @@ -11,8 +10,8 @@ object DiffrentScopeExample { case class Wrapped[T](value: T) trait Service { - def get(key: String): ZIO[Any, Nothing, Int] - def set(key: String, value: Int): ZIO[Any, Nothing, Unit] + def get(key: String): UIO[Int] + def set(key: String, value: Int): UIO[Unit] def reset: UIO[Unit] def io: IO[String, Long] def task: Task[Long] diff --git a/macros/shared/src/test/scala-2.x/zio/macros/AccessibleSpec.scala b/macros/shared/src/test/scala-2.x/zio/macros/AccessibleSpec.scala index ba63f93f0323..2be7dec99ea0 100644 --- a/macros/shared/src/test/scala-2.x/zio/macros/AccessibleSpec.scala +++ b/macros/shared/src/test/scala-2.x/zio/macros/AccessibleSpec.scala @@ -49,7 +49,7 @@ object AccessibleSpec extends DefaultRunnableSpec { @accessible object Module { trait Service { - val foo: ZIO[Any, Nothing, Unit] + val foo: UIO[Unit] } } @@ -66,7 +66,7 @@ object AccessibleSpec extends DefaultRunnableSpec { @accessible object Module { trait Service { - def foo(i: Int): ZIO[Any, Nothing, Unit] + def foo(i: Int): UIO[Unit] } } @@ -83,7 +83,7 @@ object AccessibleSpec extends DefaultRunnableSpec { @accessible object Module { trait Service { - def varargsFoo(a: Int, b: Int*): ZIO[Any, Nothing, Unit] + def varargsFoo(a: Int, b: Int*): UIO[Unit] } } @@ -100,16 +100,16 @@ object AccessibleSpec extends DefaultRunnableSpec { @accessible object Module { trait Service { - val static : ZIO[Any, Nothing, String] - def zeroArgs : ZIO[Any, Nothing, Int] - def zeroArgsWithParens() : ZIO[Any, Nothing, Long] - def singleArg(arg1: Int) : ZIO[Any, Nothing, String] - def multiArgs(arg1: Int, arg2: Long) : ZIO[Any, Nothing, String] - def multiParamLists(arg1: Int)(arg2: Long) : ZIO[Any, Nothing, String] - def typedVarargs[T](arg1: Int, arg2: T*) : ZIO[Any, Nothing, T] - def command(arg1: Int) : ZIO[Any, Nothing, Unit] - def overloaded(arg1: Int) : ZIO[Any, Nothing, String] - def overloaded(arg1: Long) : ZIO[Any, Nothing, String] + val static : UIO[String] + def zeroArgs : UIO[Int] + def zeroArgsWithParens() : UIO[Long] + def singleArg(arg1: Int) : UIO[String] + def multiArgs(arg1: Int, arg2: Long) : UIO[String] + def multiParamLists(arg1: Int)(arg2: Long) : UIO[String] + def typedVarargs[T](arg1: Int, arg2: T*) : UIO[T] + def command(arg1: Int) : UIO[Unit] + def overloaded(arg1: Int) : UIO[String] + def overloaded(arg1: Long) : UIO[String] def function(arg1: Int) : String def sink(arg1: Int) : ZSink[Any, Nothing, Int, List[Int]] def stream(arg1: Int) : ZStream[Any, Nothing, Int] diff --git a/project/build.properties b/project/build.properties index 797e7ccfdbe7..654fe70c42c7 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.3.10 +sbt.version=1.3.12 diff --git a/project/plugins.sbt b/project/plugins.sbt index 5e3f35bba6dc..8e15ff865814 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -10,9 +10,9 @@ addSbtPlugin("com.thoughtworks.sbt-api-mappings" % "sbt-api-mappings" addSbtPlugin("com.github.cb372" % "sbt-explicit-dependencies" % "0.2.13") addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.6.0") addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % "0.4.1") -addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.2.0") +addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.2.1") addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.1") addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.0-M2") addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.0.0") -addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.15") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.16") addSbtPlugin("com.geirsson" % "sbt-ci-release" % "1.5.3") diff --git a/scalafix/project/build.properties b/scalafix/project/build.properties index 797e7ccfdbe7..654fe70c42c7 100644 --- a/scalafix/project/build.properties +++ b/scalafix/project/build.properties @@ -1 +1 @@ -sbt.version=1.3.10 +sbt.version=1.3.12 diff --git a/scalafix/project/plugins.sbt b/scalafix/project/plugins.sbt index 4d3e97bac211..41ed7f69e260 100644 --- a/scalafix/project/plugins.sbt +++ b/scalafix/project/plugins.sbt @@ -1 +1 @@ -addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.15") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.16") diff --git a/streams-tests/jvm/src/test/scala/zio/stream/ZSinkPlatformSpecificSpec.scala b/streams-tests/jvm/src/test/scala/zio/stream/ZSinkPlatformSpecificSpec.scala new file mode 100644 index 000000000000..f9aff8c6ff04 --- /dev/null +++ b/streams-tests/jvm/src/test/scala/zio/stream/ZSinkPlatformSpecificSpec.scala @@ -0,0 +1,27 @@ +package zio.stream + +import java.nio.file.Files + +import zio._ +import zio.test.Assertion._ +import zio.test._ + +object ZSinkPlatformSpecificSpec extends ZIOBaseSpec { + override def spec = suite("ZSink JVM")( + suite("fromFile")( + testM("writes to an existing file") { + val data = (0 to 100).mkString + + Task(Files.createTempFile("stream", "fromFile")) + .bracket(path => Task(Files.delete(path)).orDie) { path => + for { + bytes <- Task(data.getBytes("UTF-8")) + length <- ZStream.fromIterable(bytes).run(ZSink.fromFile(path)) + str <- Task(new String(Files.readAllBytes(path))) + } yield assert(data)(equalTo(str)) && assert(bytes.length.toLong)(equalTo(length)) + } + + } + ) + ) +} diff --git a/streams-tests/jvm/src/test/scala/zio/stream/ZStreamPlatformSpecificSpec.scala b/streams-tests/jvm/src/test/scala/zio/stream/ZStreamPlatformSpecificSpec.scala index fee82a3a95a6..446669dcc103 100644 --- a/streams-tests/jvm/src/test/scala/zio/stream/ZStreamPlatformSpecificSpec.scala +++ b/streams-tests/jvm/src/test/scala/zio/stream/ZStreamPlatformSpecificSpec.scala @@ -23,12 +23,12 @@ object ZStreamPlatformSpecificSpec extends ZIOBaseSpec { def spec = suite("ZStream JVM")( suite("Constructors")( - testM("effectAsync")(checkM(Gen.listOf(Gen.anyInt)) { list => + testM("effectAsync")(checkM(Gen.chunkOf(Gen.anyInt)) { chunk => val s = ZStream.effectAsync[Any, Throwable, Int] { k => - global.execute(() => list.foreach(a => k(Task.succeed(Chunk.single(a))))) + global.execute(() => chunk.foreach(a => k(Task.succeed(Chunk.single(a))))) } - assertM(s.take(list.size.toLong).runCollect)(equalTo(list)) + assertM(s.take(chunk.size.toLong).runCollect)(equalTo(chunk)) }), suite("effectAsyncMaybe")( testM("effectAsyncMaybe signal end stream") { @@ -39,20 +39,20 @@ object ZStreamPlatformSpecificSpec extends ZIOBaseSpec { None } .runCollect - } yield assert(result)(equalTo(Nil)) + } yield assert(result)(equalTo(Chunk.empty)) }, - testM("effectAsyncMaybe Some")(checkM(Gen.listOf(Gen.anyInt)) { list => - val s = ZStream.effectAsyncMaybe[Any, Throwable, Int](_ => Some(ZStream.fromIterable(list))) + testM("effectAsyncMaybe Some")(checkM(Gen.chunkOf(Gen.anyInt)) { chunk => + val s = ZStream.effectAsyncMaybe[Any, Throwable, Int](_ => Some(ZStream.fromIterable(chunk))) - assertM(s.runCollect.map(_.take(list.size)))(equalTo(list)) + assertM(s.runCollect.map(_.take(chunk.size)))(equalTo(chunk)) }), - testM("effectAsyncMaybe None")(checkM(Gen.listOf(Gen.anyInt)) { list => + testM("effectAsyncMaybe None")(checkM(Gen.chunkOf(Gen.anyInt)) { chunk => val s = ZStream.effectAsyncMaybe[Any, Throwable, Int] { k => - global.execute(() => list.foreach(a => k(Task.succeed(Chunk.single(a))))) + global.execute(() => chunk.foreach(a => k(Task.succeed(Chunk.single(a))))) None } - assertM(s.take(list.size.toLong).runCollect)(equalTo(list)) + assertM(s.take(chunk.size.toLong).runCollect)(equalTo(chunk)) }), testM("effectAsyncMaybe back pressure") { for { @@ -82,21 +82,21 @@ object ZStreamPlatformSpecificSpec extends ZIOBaseSpec { } ), suite("effectAsyncM")( - testM("effectAsyncM")(checkM(Gen.listOf(Gen.anyInt).filter(_.nonEmpty)) { list => + testM("effectAsyncM")(checkM(Gen.chunkOf(Gen.anyInt).filter(_.nonEmpty)) { chunk => for { latch <- Promise.make[Nothing, Unit] fiber <- ZStream .effectAsyncM[Any, Throwable, Int] { k => - global.execute(() => list.foreach(a => k(Task.succeed(Chunk.single(a))))) + global.execute(() => chunk.foreach(a => k(Task.succeed(Chunk.single(a))))) latch.succeed(()) *> Task.unit } - .take(list.size.toLong) + .take(chunk.size.toLong) .run(ZSink.collectAll[Int]) .fork _ <- latch.await s <- fiber.join - } yield assert(s)(equalTo(list)) + } yield assert(s)(equalTo(chunk)) }), testM("effectAsyncM signal end stream") { for { @@ -106,7 +106,7 @@ object ZStreamPlatformSpecificSpec extends ZIOBaseSpec { UIO.unit } .runCollect - } yield assert(result)(equalTo(Nil)) + } yield assert(result)(equalTo(Chunk.empty)) }, testM("effectAsyncM back pressure") { for { @@ -148,10 +148,10 @@ object ZStreamPlatformSpecificSpec extends ZIOBaseSpec { result <- cancelled.get } yield assert(result)(isTrue) }, - testM("effectAsyncInterrupt Right")(checkM(Gen.listOf(Gen.anyInt)) { list => - val s = ZStream.effectAsyncInterrupt[Any, Throwable, Int](_ => Right(ZStream.fromIterable(list))) + testM("effectAsyncInterrupt Right")(checkM(Gen.chunkOf(Gen.anyInt)) { chunk => + val s = ZStream.effectAsyncInterrupt[Any, Throwable, Int](_ => Right(ZStream.fromIterable(chunk))) - assertM(s.take(list.size.toLong).runCollect)(equalTo(list)) + assertM(s.take(chunk.size.toLong).runCollect)(equalTo(chunk)) }), testM("effectAsyncInterrupt signal end stream ") { for { @@ -161,7 +161,7 @@ object ZStreamPlatformSpecificSpec extends ZIOBaseSpec { Left(UIO.succeedNow(())) } .runCollect - } yield assert(result)(equalTo(Nil)) + } yield assert(result)(equalTo(Chunk.empty)) }, testM("effectAsyncInterrupt back pressure") { for { diff --git a/streams-tests/shared/src/test/scala/zio/stream/SinkUtils.scala b/streams-tests/shared/src/test/scala/zio/stream/SinkUtils.scala index baebc1756979..516b2ac533f4 100644 --- a/streams-tests/shared/src/test/scala/zio/stream/SinkUtils.scala +++ b/streams-tests/shared/src/test/scala/zio/stream/SinkUtils.scala @@ -2,7 +2,7 @@ package zio.stream import zio.test.Assertion.equalTo import zio.test.{ assert, Assertion, TestResult } -import zio.{ IO, ZIO } +import zio.{ IO, UIO } object SinkUtils { @@ -16,7 +16,7 @@ object SinkUtils { stream: ZStream[Any, Nothing, A], s1: ZSink[Any, E, A, A], s2: ZSink[Any, E, A, A] - ): ZIO[Any, Nothing, TestResult] = + ): UIO[TestResult] = for { r1 <- stream.run(s1).either r2 <- stream.run(s2).either @@ -37,7 +37,7 @@ object SinkUtils { s: ZStream[Any, Nothing, A], sink1: ZSink[Any, E, A, B], sink2: ZSink[Any, E, A, C] - ): ZIO[Any, Nothing, TestResult] = + ): UIO[TestResult] = for { zb <- s.run(sink1).either zc <- s.run(sink2).either diff --git a/streams-tests/shared/src/test/scala/zio/stream/ZSinkSpec.scala b/streams-tests/shared/src/test/scala/zio/stream/ZSinkSpec.scala index edede8976738..33d615d94551 100644 --- a/streams-tests/shared/src/test/scala/zio/stream/ZSinkSpec.scala +++ b/streams-tests/shared/src/test/scala/zio/stream/ZSinkSpec.scala @@ -7,7 +7,7 @@ import zio._ import zio.stream.SinkUtils.{ findSink, sinkRaceLaw } import zio.stream.ZStreamGen._ import zio.test.Assertion.{ equalTo, isTrue, succeeds } -import zio.test._ +import zio.test.{ assertM, _ } object ZSinkSpec extends ZIOBaseSpec { def spec = suite("ZSinkSpec")( diff --git a/streams-tests/shared/src/test/scala/zio/stream/ZStreamGen.scala b/streams-tests/shared/src/test/scala/zio/stream/ZStreamGen.scala index fe6b0eaf1864..c2d9e113f703 100644 --- a/streams-tests/shared/src/test/scala/zio/stream/ZStreamGen.scala +++ b/streams-tests/shared/src/test/scala/zio/stream/ZStreamGen.scala @@ -8,6 +8,9 @@ object ZStreamGen extends GenZIO { def tinyListOf[R <: Random, A](g: Gen[R, A]): Gen[R, List[A]] = Gen.listOfBounded(0, 5)(g) + def tinyChunkOf[R <: Random, A](g: Gen[R, A]): Gen[R, Chunk[A]] = + Gen.chunkOfBounded(0, 5)(g) + def streamGen[R <: Random, A](a: Gen[R, A], max: Int): Gen[R with Sized, ZStream[Any, String, A]] = Gen.oneOf(failingStreamGen(a, max), pureStreamGen(a, max)) diff --git a/streams-tests/shared/src/test/scala/zio/stream/ZStreamSpec.scala b/streams-tests/shared/src/test/scala/zio/stream/ZStreamSpec.scala index 66b44435fd79..3c6748a251e4 100644 --- a/streams-tests/shared/src/test/scala/zio/stream/ZStreamSpec.scala +++ b/streams-tests/shared/src/test/scala/zio/stream/ZStreamSpec.scala @@ -26,15 +26,15 @@ object ZStreamSpec extends ZIOBaseSpec { suite("ZStreamSpec")( suite("Combinators")( suite("absolve")( - testM("happy path")(checkM(tinyListOf(Gen.anyInt)) { xs => + testM("happy path")(checkM(tinyChunkOf(Gen.anyInt)) { xs => val stream = ZStream.fromIterable(xs.map(Right(_))) assertM(stream.absolve.runCollect)(equalTo(xs)) }), - testM("failure")(checkM(tinyListOf(Gen.anyInt)) { xs => + testM("failure")(checkM(tinyChunkOf(Gen.anyInt)) { xs => val stream = ZStream.fromIterable(xs.map(Right(_))) ++ ZStream.succeed(Left("Ouch")) assertM(stream.absolve.runCollect.run)(fails(equalTo("Ouch"))) }), - testM("round-trip #1")(checkM(tinyListOf(Gen.anyInt), Gen.anyString) { (xs, s) => + testM("round-trip #1")(checkM(tinyChunkOf(Gen.anyInt), Gen.anyString) { (xs, s) => val xss = ZStream.fromIterable(xs.map(Right(_))) val stream = xss ++ ZStream(Left(s)) ++ xss for { @@ -42,7 +42,7 @@ object ZStreamSpec extends ZIOBaseSpec { res2 <- stream.absolve.either.runCollect } yield assert(res1)(startsWith(res2)) }), - testM("round-trip #2")(checkM(tinyListOf(Gen.anyInt), Gen.anyString) { (xs, s) => + testM("round-trip #2")(checkM(tinyChunkOf(Gen.anyInt), Gen.anyString) { (xs, s) => val xss = ZStream.fromIterable(xs) val stream = xss ++ ZStream.fail(s) for { @@ -87,7 +87,7 @@ object ZStreamSpec extends ZIOBaseSpec { .aggregateAsync(ZTransducer.foldUntil(List[Int](), 3)((acc, el) => el :: acc)) .runCollect .map { result => - assert(result.flatten)(equalTo(List(1, 1, 1, 1))) && + assert(result.toList.flatten)(equalTo(List(1, 1, 1, 1))) && assert(result.forall(_.length <= 3))(isTrue) } }, @@ -149,7 +149,7 @@ object ZStreamSpec extends ZIOBaseSpec { ) .map(_.reverse) .runCollect - .map(_.flatten) + .map(_.toList.flatten) )(equalTo(data)) } ), @@ -160,49 +160,22 @@ object ZStreamSpec extends ZIOBaseSpec { .aggregate(ZTransducer.collectAllWhile(_.isDigit)) .map(_.mkString.toInt) .runCollect - )(equalTo(List(12, 34))) + )(equalTo(Chunk(12, 34))) }, testM("no remainder") { assertM( ZStream(1, 2, 3, 4) .aggregate(ZTransducer.fold(100)(_ % 2 == 0)(_ + _)) .runCollect - )(equalTo(List(101, 105, 104))) + )(equalTo(Chunk(101, 105, 104))) }, testM("with a sink that always signals more") { assertM( ZStream(1, 2, 3) .aggregate(ZTransducer.fold(0)(_ => true)(_ + _)) .runCollect - )(equalTo(List(1 + 2 + 3))) + )(equalTo(Chunk(1 + 2 + 3))) }, - // testM("managed") { - // final class TestSink(ref: Ref[Int]) extends ZSink[Any, Throwable, Int, Int, List[Int]] { - // type State = (List[Int], Boolean) - - // def extract(state: State) = UIO.succeedNow((state._1, Chunk.empty)) - - // def initial = UIO.succeedNow((Nil, true)) - - // def step(state: State, a: Int) = - // for { - // i <- ref.get - // _ <- if (i != 1000) IO.fail(new IllegalStateException(i.toString)) else IO.unit - // } yield (List(a, a), false) - - // def cont(state: State) = state._2 - // } - - // val stream = ZStream(1, 2, 3, 4) - - // for { - // resource <- Ref.make(0) - // sink = ZManaged.make(resource.set(1000).as(new TestSink(resource)))(_ => resource.set(2000)) - // result <- stream.aggregateManaged(sink).runCollect - // i <- resource.get - // _ <- if (i != 2000) IO.fail(new IllegalStateException(i.toString)) else IO.unit - // } yield assert(result)(equalTo(List(List(1, 1), List(2, 2), List(3, 3), List(4, 4)))) - // }, testM("propagate managed error") { val fail = "I'm such a failure!" val t = ZTransducer.fail(fail) @@ -225,7 +198,7 @@ object ZStreamSpec extends ZIOBaseSpec { ) .runCollect )( - equalTo(List(Right(List(2, 1, 1, 1, 1)), Right(List(2)))) + equalTo(Chunk(Right(List(2, 1, 1, 1, 1)), Right(List(2)))) ) }, testM("error propagation 1") { @@ -288,6 +261,18 @@ object ZStreamSpec extends ZIOBaseSpec { result <- cancelled.get } yield assert(result)(isTrue) } @@ zioTag(interruption), + testM("child fiber handling") { + assertM( + ZStream + .fromSchedule(Schedule.fixed(100.millis)) + .tap(_ => TestClock.adjust(100.millis)) + .aggregateAsyncWithin(ZTransducer.last, Schedule.fixed(500.millis)) + .interruptWhen(ZIO.never) + .take(5) + .someOrFail(None) + .runCollect + )(equalTo(Chunk(3, 8, 13, 18, 23))) + } @@ zioTag(interruption), testM("aggregateAsyncWithinEitherLeftoverHandling") { val data = List(1, 2, 2, 3, 2, 3) assertM( @@ -303,7 +288,7 @@ object ZStreamSpec extends ZIOBaseSpec { case Right(v) => v } .runCollect - .map(_.flatten)) + .map(_.toList.flatten)) .fork _ <- TestClock.adjust(31.minutes) result <- f.join @@ -326,17 +311,19 @@ object ZStreamSpec extends ZIOBaseSpec { Schedule.spaced(30.minutes) ) .runCollect - )(equalTo(List(List(2, 1, 1, 1, 1), List(2)))) + )(equalTo(Chunk(List(2, 1, 1, 1, 1), List(2)))) } ), suite("bracket")( testM("bracket")( for { - done <- Ref.make(false) - iteratorStream = ZStream.bracket(UIO(0 to 2))(_ => done.set(true)).flatMap(ZStream.fromIterable(_)) - result <- iteratorStream.runCollect - released <- done.get - } yield assert(result)(equalTo(List(0, 1, 2))) && assert(released)(isTrue) + done <- Ref.make(false) + iteratorStream = ZStream + .bracket(UIO(0 to 2))(_ => done.set(true)) + .flatMap(ZStream.fromIterable(_)) + result <- iteratorStream.runCollect + released <- done.get + } yield assert(result)(equalTo(Chunk(0, 1, 2))) && assert(released)(isTrue) ), testM("bracket short circuits")( for { @@ -347,14 +334,15 @@ object ZStreamSpec extends ZIOBaseSpec { .take(2) result <- iteratorStream.runCollect released <- done.get - } yield assert(result)(equalTo(List(0, 1))) && assert(released)(isTrue) + } yield assert(result)(equalTo(Chunk(0, 1))) && assert(released)(isTrue) ), testM("no acquisition when short circuiting")( for { - acquired <- Ref.make(false) - iteratorStream = (ZStream(1) ++ ZStream.bracket(acquired.set(true))(_ => UIO.unit)).take(0) - _ <- iteratorStream.runDrain - result <- acquired.get + acquired <- Ref.make(false) + iteratorStream = (ZStream(1) ++ ZStream.bracket(acquired.set(true))(_ => UIO.unit)) + .take(0) + _ <- iteratorStream.runDrain + result <- acquired.get } yield assert(result)(isFalse) ), testM("releases when there are defects") { @@ -394,7 +382,7 @@ object ZStreamSpec extends ZIOBaseSpec { for { out1 <- s1.runCollect out2 <- s2.runCollect - expected = Range(0, 5).toList + expected = Chunk.fromIterable(Range(0, 5)) } yield assert(out1)(equalTo(expected)) && assert(out2)(equalTo(expected)) case _ => UIO(assert(())(Assertion.nothing)) @@ -419,9 +407,12 @@ object ZStreamSpec extends ZIOBaseSpec { .use { case s1 :: s2 :: Nil => for { - ref <- Ref.make[List[Int]](Nil) - latch1 <- Promise.make[Nothing, Unit] - fib <- s1.tap(i => ref.update(i :: _) *> latch1.succeed(()).when(i == 2)).runDrain.fork + ref <- Ref.make[List[Int]](Nil) + latch1 <- Promise.make[Nothing, Unit] + fib <- s1 + .tap(i => ref.update(i :: _) *> latch1.succeed(()).when(i == 2)) + .runDrain + .fork _ <- latch1.await snapshot1 <- ref.get _ <- s2.runDrain @@ -440,20 +431,20 @@ object ZStreamSpec extends ZIOBaseSpec { for { _ <- s1.process.use_(ZIO.unit).ignore out2 <- s2.runCollect - } yield assert(out2)(equalTo(Range(0, 5).toList)) + } yield assert(out2)(equalTo(Chunk.fromIterable(Range(0, 5)))) case _ => UIO(assert(())(Assertion.nothing)) } } ), suite("buffer")( - testM("maintains elements and ordering")(checkM(tinyListOf(Gen.chunkOf(Gen.anyInt))) { list => + testM("maintains elements and ordering")(checkM(tinyChunkOf(Gen.chunkOf(Gen.anyInt))) { chunk => assertM( ZStream - .fromChunks(list: _*) + .fromChunks(chunk: _*) .buffer(2) .runCollect - )(equalTo(Chunk.fromIterable(list).flatten.toList)) + )(equalTo(chunk.flatten)) }), testM("buffer the Stream with Error") { val e = new RuntimeException("boom") @@ -468,7 +459,10 @@ object ZStreamSpec extends ZIOBaseSpec { for { ref <- Ref.make(List[Int]()) latch <- Promise.make[Nothing, Unit] - s = ZStream.range(1, 5).tap(i => ref.update(i :: _) *> latch.succeed(()).when(i == 4)).buffer(2) + s = ZStream + .range(1, 5) + .tap(i => ref.update(i :: _) *> latch.succeed(()).when(i == 4)) + .buffer(2) l <- s.process.use { as => for { _ <- as @@ -519,7 +513,9 @@ object ZStreamSpec extends ZIOBaseSpec { } yield assert(snapshots._1)(equalTo(Chunk.single(0))) && assert(snapshots._2)( equalTo(List(8, 7, 6, 5, 4, 3, 2, 1)) ) && - assert(snapshots._3)(equalTo(List(24, 23, 22, 21, 20, 19, 18, 17, 8, 7, 6, 5, 4, 3, 2, 1))) + assert(snapshots._3)( + equalTo(List(24, 23, 22, 21, 20, 19, 18, 17, 8, 7, 6, 5, 4, 3, 2, 1)) + ) } ), suite("bufferSliding")( @@ -562,21 +558,25 @@ object ZStreamSpec extends ZIOBaseSpec { } yield assert(snapshots._1)(equalTo(Chunk.single(0))) && assert(snapshots._2)( equalTo(List(16, 15, 14, 13, 12, 11, 10, 9)) ) && - assert(snapshots._3)(equalTo(List(24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9))) + assert(snapshots._3)( + equalTo(List(24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9)) + ) } ), suite("bufferUnbounded")( - testM("buffer the Stream")(checkM(Gen.listOf(Gen.anyInt)) { list => + testM("buffer the Stream")(checkM(Gen.chunkOf(Gen.anyInt)) { chunk => assertM( ZStream - .fromIterable(list) + .fromIterable(chunk) .bufferUnbounded .runCollect - )(equalTo(list)) + )(equalTo(chunk)) }), testM("buffer the Stream with Error") { val e = new RuntimeException("boom") - assertM((ZStream.range(0, 10) ++ ZStream.fail(e)).bufferUnbounded.runCollect.run)(fails(equalTo(e))) + assertM((ZStream.range(0, 10) ++ ZStream.fail(e)).bufferUnbounded.runCollect.run)( + fails(equalTo(e)) + ) }, testM("fast producer progress independently") { for { @@ -600,17 +600,17 @@ object ZStreamSpec extends ZIOBaseSpec { testM("recovery from errors") { val s1 = ZStream(1, 2) ++ ZStream.fail("Boom") val s2 = ZStream(3, 4) - s1.catchAllCause(_ => s2).runCollect.map(assert(_)(equalTo(List(1, 2, 3, 4)))) + s1.catchAllCause(_ => s2).runCollect.map(assert(_)(equalTo(Chunk(1, 2, 3, 4)))) }, testM("recovery from defects") { val s1 = ZStream(1, 2) ++ ZStream.dieMessage("Boom") val s2 = ZStream(3, 4) - s1.catchAllCause(_ => s2).runCollect.map(assert(_)(equalTo(List(1, 2, 3, 4)))) + s1.catchAllCause(_ => s2).runCollect.map(assert(_)(equalTo(Chunk(1, 2, 3, 4)))) }, testM("happy path") { val s1 = ZStream(1, 2) val s2 = ZStream(3, 4) - s1.catchAllCause(_ => s2).runCollect.map(assert(_)(equalTo(List(1, 2)))) + s1.catchAllCause(_ => s2).runCollect.map(assert(_)(equalTo(Chunk(1, 2)))) }, testM("executes finalizers") { for { @@ -623,13 +623,13 @@ object ZStreamSpec extends ZIOBaseSpec { }, testM("releases all resources by the time the failover stream has started") { for { - fins <- Ref.make(List[Int]()) - s = ZStream.finalizer(fins.update(1 :: _)) *> - ZStream.finalizer(fins.update(2 :: _)) *> - ZStream.finalizer(fins.update(3 :: _)) *> + fins <- Ref.make(Chunk[Int]()) + s = ZStream.finalizer(fins.update(1 +: _)) *> + ZStream.finalizer(fins.update(2 +: _)) *> + ZStream.finalizer(fins.update(3 +: _)) *> ZStream.fail("boom") result <- s.drain.catchAllCause(_ => ZStream.fromEffect(fins.get)).runCollect - } yield assert(result.flatten)(equalTo(List(1, 2, 3))) + } yield assert(result.flatten)(equalTo(Chunk(1, 2, 3))) }, testM("propagates the right Exit value to the failing stream (#3609)") { for { @@ -648,30 +648,33 @@ object ZStreamSpec extends ZIOBaseSpec { testM("recovery from some errors") { val s1 = ZStream(1, 2) ++ ZStream.fail("Boom") val s2 = ZStream(3, 4) - s1.catchSome { case "Boom" => s2 }.runCollect.map(assert(_)(equalTo(List(1, 2, 3, 4)))) + s1.catchSome { case "Boom" => s2 }.runCollect.map(assert(_)(equalTo(Chunk(1, 2, 3, 4)))) }, testM("fails stream when partial function does not match") { val s1 = ZStream(1, 2) ++ ZStream.fail("Boom") val s2 = ZStream(3, 4) - s1.catchSome { case "Boomer" => s2 }.runCollect.either.map(assert(_)(isLeft(equalTo("Boom")))) + s1.catchSome { case "Boomer" => s2 }.runCollect.either + .map(assert(_)(isLeft(equalTo("Boom")))) } ), suite("catchSomeCause")( testM("recovery from some errors") { val s1 = ZStream(1, 2) ++ ZStream.halt(Cause.Fail("Boom")) val s2 = ZStream(3, 4) - s1.catchSomeCause { case Cause.Fail("Boom") => s2 }.runCollect.map(assert(_)(equalTo(List(1, 2, 3, 4)))) + s1.catchSomeCause { case Cause.Fail("Boom") => s2 }.runCollect + .map(assert(_)(equalTo(Chunk(1, 2, 3, 4)))) }, testM("halts stream when partial function does not match") { val s1 = ZStream(1, 2) ++ ZStream.fail("Boom") val s2 = ZStream(3, 4) - s1.catchSomeCause { case Cause.empty => s2 }.runCollect.either.map(assert(_)(isLeft(equalTo("Boom")))) + s1.catchSomeCause { case Cause.empty => s2 }.runCollect.either + .map(assert(_)(isLeft(equalTo("Boom")))) } ), testM("collect") { assertM(ZStream(Left(1), Right(2), Left(3)).collect { case Right(n) => n - }.runCollect)(equalTo(List(2))) + }.runCollect)(equalTo(Chunk(2))) }, suite("collectM")( testM("collectM") { @@ -679,7 +682,7 @@ object ZStreamSpec extends ZIOBaseSpec { ZStream(Left(1), Right(2), Left(3)).collectM { case Right(n) => ZIO(n * 2) }.runCollect - )(equalTo(List(4))) + )(equalTo(Chunk(4))) }, testM("collectM fails") { assertM( @@ -694,25 +697,27 @@ object ZStreamSpec extends ZIOBaseSpec { case 3 => ZIO.fail("boom") case x => UIO.succeed(x) }.either.runCollect - )(equalTo(List(Right(1), Right(2), Left("boom")))) + )(equalTo(Chunk(Right(1), Right(2), Left("boom")))) } ), testM("collectSome")(checkM(Gen.bounded(0, 5)(pureStreamGen(Gen.option(Gen.anyInt), _))) { s => for { res1 <- (s.collectSome.runCollect) - res2 <- (s.runCollect.map(_.flatten)) + res2 <- (s.runCollect.map(_.collect { case Some(x) => x })) } yield assert(res1)(equalTo(res2)) }), suite("collectWhile")( testM("collectWhile") { assertM(ZStream(Some(1), Some(2), Some(3), None, Some(4)).collectWhile { case Some(v) => v - }.runCollect)(equalTo(List(1, 2, 3))) + }.runCollect)(equalTo(Chunk(1, 2, 3))) }, testM("collectWhile short circuits") { - assertM((ZStream(Option(1)) ++ ZStream.fail("Ouch")).collectWhile { - case None => 1 - }.runDrain.either)(isRight(isUnit)) + assertM( + (ZStream(Option(1)) ++ ZStream.fail("Ouch")).collectWhile { + case None => 1 + }.runDrain.either + )(isRight(isUnit)) } ), suite("collectWhileM")( @@ -721,7 +726,7 @@ object ZStreamSpec extends ZIOBaseSpec { ZStream(Some(1), Some(2), Some(3), None, Some(4)).collectWhileM { case Some(v) => ZIO(v * 2) }.runCollect - )(equalTo(List(2, 4, 6))) + )(equalTo(Chunk(2, 4, 6))) }, testM("collectWhileM short circuits") { assertM( @@ -746,7 +751,7 @@ object ZStreamSpec extends ZIOBaseSpec { case 3 => ZIO.fail("boom") case x => UIO.succeed(x) }.either.runCollect - )(equalTo(List(Right(1), Right(2), Left("boom")))) + )(equalTo(Chunk(Right(1), Right(2), Left("boom")))) } ), suite("collectWhileSuccess")( @@ -757,7 +762,7 @@ object ZStreamSpec extends ZIOBaseSpec { .toQueue(1) .use(q => ZStream.fromQueue(q).map(_.exit).collectWhileSuccess.runCollect) .map(_.flatMap(_.toList)) - )(equalTo(Range(0, 10).toList)) + )(equalTo(Chunk.fromIterable(Range(0, 10)))) }, testM("errors") { val e = new RuntimeException("boom") @@ -772,10 +777,12 @@ object ZStreamSpec extends ZIOBaseSpec { suite("concat")( testM("concat")(checkM(streamOfBytes, streamOfBytes) { (s1, s2) => for { - listConcat <- s1.runCollect.zipWith(s2.runCollect)(_ ++ _).run + chunkConcat <- s1.runCollect.zipWith(s2.runCollect)(_ ++ _).run streamConcat <- (s1 ++ s2).runCollect.run - } yield assert(streamConcat.succeeded && listConcat.succeeded)(isTrue) implies assert(streamConcat)( - equalTo(listConcat) + } yield assert(streamConcat.succeeded && chunkConcat.succeeded)(isTrue) implies assert( + streamConcat + )( + equalTo(chunkConcat) ) }), testM("finalizer order") { @@ -831,14 +838,18 @@ object ZStreamSpec extends ZIOBaseSpec { latch <- Promise.make[Nothing, Unit] _ <- (ZStream(1, 2, 3) ++ ZStream.fromEffect(latch.await).drain) .drainFork( - ZStream.fromEffect((latch.succeed(()) *> ZIO.never).onInterrupt(bgInterrupted.set(true))) + ZStream.fromEffect( + (latch.succeed(()) *> ZIO.never).onInterrupt(bgInterrupted.set(true)) + ) ) .runDrain result <- bgInterrupted.get } yield assert(result)(isTrue) } @@ zioTag(interruption), testM("fails the foreground stream if the background fails with a typed error") { - assertM(ZStream.never.drainFork(ZStream.fail("Boom")).runDrain.run)(fails(equalTo("Boom"))) + assertM(ZStream.never.drainFork(ZStream.fail("Boom")).runDrain.run)( + fails(equalTo("Boom")) + ) } @@ zioTag(errors), testM("fails the foreground stream if the background fails with a defect") { val ex = new RuntimeException("Boom") @@ -849,7 +860,9 @@ object ZStreamSpec extends ZIOBaseSpec { for { dropStreamResult <- s.drop(n.toLong).runCollect.run dropListResult <- s.runCollect.map(_.drop(n)).run - } yield assert(dropListResult.succeeded)(isTrue) implies assert(dropStreamResult)(equalTo(dropListResult)) + } yield assert(dropListResult.succeeded)(isTrue) implies assert(dropStreamResult)( + equalTo(dropListResult) + ) }), testM("dropUntil") { checkM(pureStreamOfBytes, Gen.function(Gen.boolean)) { (s, p) => @@ -881,7 +894,8 @@ object ZStreamSpec extends ZIOBaseSpec { ), testM("either") { val s = ZStream(1, 2, 3) ++ ZStream.fail("Boom") - s.either.runCollect.map(assert(_)(equalTo(List(Right(1), Right(2), Right(3), Left("Boom"))))) + s.either.runCollect + .map(assert(_)(equalTo(Chunk(Right(1), Right(2), Right(3), Left("Boom"))))) }, testM("ensuring") { for { @@ -922,7 +936,7 @@ object ZStreamSpec extends ZIOBaseSpec { case 3 => ZIO.fail("boom") case _ => UIO.succeed(true) }.either.runCollect - )(equalTo(List(Right(1), Right(2), Left("boom")))) + )(equalTo(Chunk(Right(1), Right(2), Left("boom")))) } ), suite("flatMap")( @@ -935,7 +949,7 @@ object ZStreamSpec extends ZIOBaseSpec { val stream = fib(20) val expected = 6765 - assertM(stream.runCollect)(equalTo(List(expected))) + assertM(stream.runCollect)(equalTo(Chunk(expected))) } @@ TestAspect.jvmOnly, // Too slow on Scala.js testM("left identity")(checkM(Gen.anyInt, Gen.function(pureStreamOfInts)) { (x, f) => for { @@ -985,9 +999,15 @@ object ZStreamSpec extends ZIOBaseSpec { push = (i: String) => effects.update(i :: _) stream = for { _ <- ZStream.bracket(push("open1"))(_ => push("close1")) - _ <- ZStream.fromChunks(Chunk(()), Chunk(())).tap(_ => push("use2")).ensuring(push("close2")) + _ <- ZStream + .fromChunks(Chunk(()), Chunk(())) + .tap(_ => push("use2")) + .ensuring(push("close2")) _ <- ZStream.bracket(push("open3"))(_ => push("close3")) - _ <- ZStream.fromChunks(Chunk(()), Chunk(())).tap(_ => push("use4")).ensuring(push("close4")) + _ <- ZStream + .fromChunks(Chunk(()), Chunk(())) + .tap(_ => push("use4")) + .ensuring(push("close4")) } yield () _ <- stream.runDrain result <- effects.get @@ -1056,13 +1076,22 @@ object ZStreamSpec extends ZIOBaseSpec { flatMapPar <- ZStream.fromIterable(m).flatMapPar(1)(i => ZStream(i, i)).runCollect } yield assert(flatMap)(equalTo(flatMapPar)) }), - testM("consistent with flatMap")(checkM(Gen.int(1, Int.MaxValue), Gen.small(Gen.listOfN(_)(Gen.anyInt))) { - (n, m) => + testM("consistent with flatMap")( + checkM(Gen.int(1, Int.MaxValue), Gen.small(Gen.listOfN(_)(Gen.anyInt))) { (n, m) => for { - flatMap <- ZStream.fromIterable(m).flatMap(i => ZStream(i, i)).runCollect.map(_.toSet) - flatMapPar <- ZStream.fromIterable(m).flatMapPar(n)(i => ZStream(i, i)).runCollect.map(_.toSet) + flatMap <- ZStream + .fromIterable(m) + .flatMap(i => ZStream(i, i)) + .runCollect + .map(_.toSet) + flatMapPar <- ZStream + .fromIterable(m) + .flatMapPar(n)(i => ZStream(i, i)) + .runCollect + .map(_.toSet) } yield assert(n)(isGreaterThan(0)) implies assert(flatMap)(equalTo(flatMapPar)) - }), + } + ), testM("short circuiting") { assertM( ZStream @@ -1072,7 +1101,7 @@ object ZStreamSpec extends ZIOBaseSpec { ) .take(1) .runCollect - )(equalTo(List(1))) + )(equalTo(Chunk(1))) }, testM("interruption propagation") { for { @@ -1163,7 +1192,9 @@ object ZStreamSpec extends ZIOBaseSpec { .flatMapPar(2)(identity) .runDrain results <- execution.get - } yield assert(results)(equalTo(List("OuterRelease", "InnerRelease", "InnerAcquire", "OuterAcquire"))) + } yield assert(results)( + equalTo(List("OuterRelease", "InnerRelease", "InnerAcquire", "OuterAcquire")) + ) } ), suite("flatMapParSwitch")( @@ -1173,7 +1204,10 @@ object ZStreamSpec extends ZIOBaseSpec { semaphore <- Semaphore.make(1) _ <- ZStream(1, 2, 3, 4) .flatMapParSwitch(1) { i => - if (i > 3) ZStream.bracket(UIO.unit)(_ => lastExecuted.set(true)).flatMap(_ => ZStream.empty) + if (i > 3) + ZStream + .bracket(UIO.unit)(_ => lastExecuted.set(true)) + .flatMap(_ => ZStream.empty) else ZStream.managed(semaphore.withPermitManaged).flatMap(_ => ZStream.never) } .runDrain @@ -1187,7 +1221,9 @@ object ZStreamSpec extends ZIOBaseSpec { _ <- ZStream(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) .flatMapParSwitch(4) { i => if (i > 8) - ZStream.bracket(UIO.unit)(_ => lastExecuted.update(_ + 1)).flatMap(_ => ZStream.empty) + ZStream + .bracket(UIO.unit)(_ => lastExecuted.update(_ + 1)) + .flatMap(_ => ZStream.empty) else ZStream.managed(semaphore.withPermitManaged).flatMap(_ => ZStream.never) } .runDrain @@ -1200,7 +1236,7 @@ object ZStreamSpec extends ZIOBaseSpec { .flatMapParSwitch(2)(identity) .take(1) .runCollect - )(equalTo(List(1))) + )(equalTo(Chunk(1))) }, testM("interruption propagation") { for { @@ -1290,7 +1326,9 @@ object ZStreamSpec extends ZIOBaseSpec { .flatMapParSwitch(2)(identity) .runDrain results <- execution.get - } yield assert(results)(equalTo(List("OuterRelease", "InnerRelease", "InnerAcquire", "OuterAcquire"))) + } yield assert(results)( + equalTo(List("OuterRelease", "InnerRelease", "InnerAcquire", "OuterAcquire")) + ) } ), suite("flattenTake")( @@ -1301,7 +1339,7 @@ object ZStreamSpec extends ZIOBaseSpec { .mapChunks(chunk => Chunk.single(Take.chunk(chunk))) .flattenTake .runCollect - )(equalTo(chunks.fold(Chunk.empty)(_ ++ _).toList)) + )(equalTo(chunks.fold(Chunk.empty)(_ ++ _))) }), testM("stop collecting on Exit.Failure") { assertM( @@ -1310,13 +1348,17 @@ object ZStreamSpec extends ZIOBaseSpec { Take.single(3), Take.end ).flattenTake.runCollect - )(equalTo(List(1, 2, 3))) + )(equalTo(Chunk(1, 2, 3))) }, testM("work with empty chunks") { - assertM(ZStream(Take.chunk(Chunk.empty), Take.chunk(Chunk.empty)).flattenTake.runCollect)(isEmpty) + assertM( + ZStream(Take.chunk(Chunk.empty), Take.chunk(Chunk.empty)).flattenTake.runCollect + )(isEmpty) }, testM("work with empty streams") { - assertM(ZStream.fromIterable[Take[Nothing, Nothing]](Nil).flattenTake.runCollect)(isEmpty) + assertM(ZStream.fromIterable[Take[Nothing, Nothing]](Nil).flattenTake.runCollect)( + isEmpty + ) } ), suite("foreach")( @@ -1484,7 +1526,7 @@ object ZStreamSpec extends ZIOBaseSpec { _ <- c.offer result <- fiber.join } yield result - )(equalTo(List(Chunk(1), Chunk(2), Chunk(3)))) + )(equalTo(Chunk(Chunk(1), Chunk(2), Chunk(3)))) } }, testM("will process first chunk") { @@ -1494,11 +1536,11 @@ object ZStreamSpec extends ZIOBaseSpec { _ <- TestClock.adjust(6.seconds) _ <- queue.offer(1) result <- fiber.join - } yield assert(result)(equalTo(List(1))) + } yield assert(result)(equalTo(Chunk(1))) } ), testM("grouped")( - assertM(ZStream(1, 2, 3, 4).grouped(2).runCollect)(equalTo(List(List(1, 2), List(3, 4)))) + assertM(ZStream(1, 2, 3, 4).grouped(2).runCollect)(equalTo(Chunk(List(1, 2), List(3, 4)))) ), suite("groupedWithin")( testM("group based on time passed") { @@ -1516,38 +1558,40 @@ object ZStreamSpec extends ZIOBaseSpec { _ <- c.offer *> TestClock.adjust(2.seconds) *> c.awaitNext _ <- c.offer result <- f.join - } yield result)(equalTo(List(List(1, 2), List(3, 4), List(5)))) + } yield result)(equalTo(Chunk(List(1, 2), List(3, 4), List(5)))) } } @@ timeout(10.seconds) @@ flaky, testM("group immediately when chunk size is reached") { - assertM(ZStream(1, 2, 3, 4).groupedWithin(2, 10.seconds).runCollect)(equalTo(List(List(1, 2), List(3, 4)))) + assertM(ZStream(1, 2, 3, 4).groupedWithin(2, 10.seconds).runCollect)( + equalTo(Chunk(List(1, 2), List(3, 4))) + ) } ), testM("interleave") { val s1 = ZStream(2, 3) val s2 = ZStream(5, 6, 7) - assertM(s1.interleave(s2).runCollect)(equalTo(List(2, 5, 3, 6, 7))) + assertM(s1.interleave(s2).runCollect)(equalTo(Chunk(2, 5, 3, 6, 7))) }, testM("interleaveWith") { - def interleave(b: List[Boolean], s1: => List[Int], s2: => List[Int]): List[Int] = + def interleave(b: Chunk[Boolean], s1: => Chunk[Int], s2: => Chunk[Int]): Chunk[Int] = b.headOption.map { hd => if (hd) s1 match { - case h :: t => - h :: interleave(b.tail, t, s2) + case h +: t => + h +: interleave(b.tail, t, s2) case _ => - if (s2.isEmpty) List.empty - else interleave(b.tail, List.empty, s2) + if (s2.isEmpty) Chunk.empty + else interleave(b.tail, Chunk.empty, s2) } else s2 match { - case h :: t => - h :: interleave(b.tail, s1, t) + case h +: t => + h +: interleave(b.tail, s1, t) case _ => - if (s1.isEmpty) List.empty - else interleave(b.tail, s1, List.empty) + if (s1.isEmpty) Chunk.empty + else interleave(b.tail, s1, Chunk.empty) } - }.getOrElse(List.empty) + }.getOrElse(Chunk.empty) val int = Gen.int(0, 5) @@ -1571,28 +1615,74 @@ object ZStreamSpec extends ZIOBaseSpec { .map(_.toString) .intersperse("@") .runCollect - .map(result => assert(result)(equalTo(List("1", "@", "2", "@", "3", "@", "4")))) + .map(result => assert(result)(equalTo(Chunk("1", "@", "2", "@", "3", "@", "4")))) }, testM("intersperse several with begin and end") { Stream(1, 2, 3, 4) .map(_.toString) .intersperse("[", "@", "]") .runCollect - .map(result => assert(result)(equalTo(List("[", "1", "@", "2", "@", "3", "@", "4", "]")))) + .map(result => assert(result)(equalTo(Chunk("[", "1", "@", "2", "@", "3", "@", "4", "]")))) }, testM("intersperse single") { Stream(1) .map(_.toString) .intersperse("@") .runCollect - .map(result => assert(result)(equalTo(List("1")))) + .map(result => assert(result)(equalTo(Chunk("1")))) }, testM("intersperse single with begin and end") { Stream(1) .map(_.toString) .intersperse("[", "@", "]") .runCollect - .map(result => assert(result)(equalTo(List("[", "1", "]")))) + .map(result => assert(result)(equalTo(Chunk("[", "1", "]")))) + }, + testM("mkString(Sep) equivalence") { + checkM( + Gen + .int(0, 10) + .flatMap(Gen.listOfN(_)(Gen.small(Gen.chunkOfN(_)(Gen.anyInt)))) + ) { chunks => + val stream = ZStream.fromChunks(chunks: _*) + + for { + interspersed <- stream.map(_.toString).intersperse("@").runCollect.map(_.mkString) + regular <- stream.map(_.toString).runCollect.map(_.mkString("@")) + } yield assert(interspersed)(equalTo(regular)) + } + }, + testM("mkString(Before, Sep, After) equivalence") { + checkM( + Gen + .int(0, 10) + .flatMap(Gen.listOfN(_)(Gen.small(Gen.chunkOfN(_)(Gen.anyInt)))) + ) { chunks => + val stream = ZStream.fromChunks(chunks: _*) + + for { + interspersed <- stream.map(_.toString).intersperse("[", "@", "]").runCollect.map(_.mkString) + regular <- stream.map(_.toString).runCollect.map(_.mkString("[", "@", "]")) + } yield assert(interspersed)(equalTo(regular)) + } + }, + testM("intersperse several from repeat effect (#3729)") { + Stream + .repeatEffect(ZIO.succeed(42)) + .map(_.toString) + .take(4) + .intersperse("@") + .runCollect + .map(result => assert(result)(equalTo(Chunk("42", "@", "42", "@", "42", "@", "42")))) + }, + testM("intersperse several from repeat effect chunk single element (#3729)") { + Stream + .repeatEffectChunk(ZIO.succeed(Chunk(42))) + .map(_.toString) + .intersperse("@") + .take(4) + .runCollect + .map(result => assert(result)(equalTo(Chunk("42", "@", "42", "@")))) } ), suite("interruptWhen")( @@ -1604,7 +1694,9 @@ object ZStreamSpec extends ZIOBaseSpec { halt <- Promise.make[Nothing, Unit] started <- Promise.make[Nothing, Unit] fiber <- ZStream - .fromEffect((started.succeed(()) *> latch.await).onInterrupt(interrupted.set(true))) + .fromEffect( + (started.succeed(()) *> latch.await).onInterrupt(interrupted.set(true)) + ) .interruptWhen(halt) .runDrain .fork @@ -1632,7 +1724,9 @@ object ZStreamSpec extends ZIOBaseSpec { halt <- Promise.make[Nothing, Unit] started <- Promise.make[Nothing, Unit] fiber <- ZStream - .fromEffect((started.succeed(()) *> latch.await).onInterrupt(interrupted.set(true))) + .fromEffect( + (started.succeed(()) *> latch.await).onInterrupt(interrupted.set(true)) + ) .interruptWhen(halt.await) .runDrain .fork @@ -1670,7 +1764,7 @@ object ZStreamSpec extends ZIOBaseSpec { _ <- c.offer result <- fiber.join } yield result - )(equalTo(List(Chunk(1), Chunk(2)))) + )(equalTo(Chunk(Chunk(1), Chunk(2)))) } }, testM("interrupts before first chunk") { @@ -1686,7 +1780,9 @@ object ZStreamSpec extends ZIOBaseSpec { suite("managed")( testM("preserves interruptibility of effect") { for { - interruptible <- ZStream.managed(ZManaged.fromEffect(ZIO.checkInterruptible(UIO.succeed(_)))).runHead + interruptible <- ZStream + .managed(ZManaged.fromEffect(ZIO.checkInterruptible(UIO.succeed(_)))) + .runHead uninterruptible <- ZStream .managed(ZManaged.fromEffectUninterruptible(ZIO.checkInterruptible(UIO.succeed(_)))) .runHead @@ -1701,7 +1797,9 @@ object ZStreamSpec extends ZIOBaseSpec { } yield assert(res1)(equalTo(res2)) }), testM("mapAccum") { - assertM(ZStream(1, 1, 1).mapAccum(0)((acc, el) => (acc + el, acc + el)).runCollect)(equalTo(List(1, 2, 3))) + assertM(ZStream(1, 1, 1).mapAccum(0)((acc, el) => (acc + el, acc + el)).runCollect)( + equalTo(Chunk(1, 2, 3)) + ) }, suite("mapAccumM")( testM("mapAccumM happy path") { @@ -1709,7 +1807,7 @@ object ZStreamSpec extends ZIOBaseSpec { ZStream(1, 1, 1) .mapAccumM[Any, Nothing, Int, Int](0)((acc, el) => IO.succeed((acc + el, acc + el))) .runCollect - )(equalTo(List(1, 2, 3))) + )(equalTo(Chunk(1, 2, 3))) }, testM("mapAccumM error") { ZStream(1, 1, 1) @@ -1727,7 +1825,7 @@ object ZStreamSpec extends ZIOBaseSpec { } .either .runCollect - )(equalTo(List(Right(1), Right(2), Left("boom")))) + )(equalTo(Chunk(Right(1), Right(2), Left("boom")))) } ), testM("mapConcat")(checkM(pureStreamOfBytes, Gen.function(Gen.listOf(Gen.anyInt))) { (s, f) => @@ -1800,7 +1898,7 @@ object ZStreamSpec extends ZIOBaseSpec { for { l <- s.mapM(f).runCollect r <- IO.foreach(data)(f) - } yield assert(l)(equalTo(r)) + } yield assert(l.toList)(equalTo(r)) } }, testM("laziness on chunks") { @@ -1809,7 +1907,7 @@ object ZStreamSpec extends ZIOBaseSpec { case 3 => ZIO.fail("boom") case x => UIO.succeed(x) }.either.runCollect - )(equalTo(List(Right(1), Right(2), Left("boom")))) + )(equalTo(Chunk(Right(1), Right(2), Left("boom")))) } ), suite("mapMPar")( @@ -1820,7 +1918,7 @@ object ZStreamSpec extends ZIOBaseSpec { for { l <- s.mapMPar(8)(f).runCollect r <- IO.foreachParN(8)(data)(f) - } yield assert(l)(equalTo(r)) + } yield assert(l.toList)(equalTo(r)) } }, testM("order when n = 1") { @@ -1848,7 +1946,18 @@ object ZStreamSpec extends ZIOBaseSpec { mapM <- ZStream.fromIterable(m).mapM(UIO.succeedNow).runCollect mapMPar <- ZStream.fromIterable(m).mapMPar(n)(UIO.succeedNow).runCollect } yield assert(n)(isGreaterThan(0)) implies assert(mapM)(equalTo(mapMPar)) - }) + }), + testM("awaits children fibers properly") { + assertM( + ZStream + .fromIterable((0 to 100)) + .interruptWhen(ZIO.never) + .mapMPar(8)(_ => ZIO(1).repeat(Schedule.recurs(2000))) + .runDrain + .run + .map(_.interrupted) + )(equalTo(false)) + } @@ nonFlaky(10) ), suite("mergeTerminateLeft")( testM("terminates as soon as the first stream terminates") { @@ -1863,12 +1972,12 @@ object ZStreamSpec extends ZIOBaseSpec { _ <- queue1.shutdown *> TestClock.adjust(1.second) _ <- queue2.offer(3) result <- fiber.join - } yield assert(result)(equalTo(List(1, 2))) + } yield assert(result)(equalTo(Chunk(1, 2))) }, testM("interrupts pulling on finish") { val s1 = ZStream(1, 2, 3) val s2 = ZStream.fromEffect(clock.sleep(5.seconds).as(4)) - assertM(s1.mergeTerminateLeft(s2).runCollect)(equalTo(List(1, 2, 3))) + assertM(s1.mergeTerminateLeft(s2).runCollect)(equalTo(Chunk(1, 2, 3))) } ), suite("mergeTerminateRight")( @@ -1884,7 +1993,7 @@ object ZStreamSpec extends ZIOBaseSpec { _ <- queue2.shutdown *> TestClock.adjust(1.second) _ <- queue1.offer(1) result <- fiber.join - } yield assert(result)(equalTo(List(2, 3))) + } yield assert(result)(equalTo(Chunk(2, 3))) } ), suite("mergeTerminateEither")( @@ -1911,12 +2020,16 @@ object ZStreamSpec extends ZIOBaseSpec { .zipWith(s2.runCollect)((left, right) => left ++ right) .map(_.toSet) .run - } yield assert(!mergedStream.succeeded && !mergedLists.succeeded)(isTrue) || assert(mergedStream)( + } yield assert(!mergedStream.succeeded && !mergedLists.succeeded)(isTrue) || assert( + mergedStream + )( equalTo(mergedLists) ) }), testM("fail as soon as one stream fails") { - assertM(ZStream(1, 2, 3).merge(ZStream.fail(())).runCollect.run.map(_.succeeded))(equalTo(false)) + assertM(ZStream(1, 2, 3).merge(ZStream.fail(())).runCollect.run.map(_.succeeded))( + equalTo(false) + ) } @@ nonFlaky(20), testM("prioritizes failure") { val s1 = ZStream.never @@ -1946,7 +2059,9 @@ object ZStreamSpec extends ZIOBaseSpec { for { out1 <- s1.runCollect out2 <- s2.runCollect - } yield assert(out1)(equalTo(List(0, 2, 4))) && assert(out2)(equalTo(List(1, 3, 5))) + } yield assert(out1)(equalTo(Chunk(0, 2, 4))) && assert(out2)( + equalTo(Chunk(1, 3, 5)) + ) } }, testM("errors") { @@ -1958,7 +2073,9 @@ object ZStreamSpec extends ZIOBaseSpec { for { out1 <- s1.runCollect.either out2 <- s2.runCollect.either - } yield assert(out1)(isLeft(equalTo("Boom"))) && assert(out2)(isLeft(equalTo("Boom"))) + } yield assert(out1)(isLeft(equalTo("Boom"))) && assert(out2)( + isLeft(equalTo("Boom")) + ) } }, testM("backpressure") { @@ -1973,19 +2090,24 @@ object ZStreamSpec extends ZIOBaseSpec { .use { case (s1, s2) => for { - ref <- Ref.make[List[Int]](Nil) - latch1 <- Promise.make[Nothing, Unit] - fib <- s1.tap(i => ref.update(i :: _) *> latch1.succeed(()).when(i == 2)).runDrain.fork + ref <- Ref.make[List[Int]](Nil) + latch1 <- Promise.make[Nothing, Unit] + fib <- s1 + .tap(i => ref.update(i :: _) *> latch1.succeed(()).when(i == 2)) + .runDrain + .fork _ <- latch1.await snapshot1 <- ref.get other <- s2.runCollect _ <- fib.await snapshot2 <- ref.get - } yield assert(snapshot1)(equalTo(List(2, 0))) && assert(snapshot2)(equalTo(List(4, 2, 0))) && assert( + } yield assert(snapshot1)(equalTo(List(2, 0))) && assert(snapshot2)( + equalTo(List(4, 2, 0)) + ) && assert( other )( equalTo( - List( + Chunk( 1, 3, 5 @@ -2007,19 +2129,19 @@ object ZStreamSpec extends ZIOBaseSpec { case (chunk, rest) => rest.runCollect.map { rest => assert(chunk)(equalTo(Chunk(1, 2, 3))) && - assert(rest)(equalTo(List(4, 5, 6))) + assert(rest)(equalTo(Chunk(4, 5, 6))) } } }, testM("orElse") { val s1 = ZStream(1, 2, 3) ++ ZStream.fail("Boom") val s2 = ZStream(4, 5, 6) - s1.orElse(s2).runCollect.map(assert(_)(equalTo(List(1, 2, 3, 4, 5, 6)))) + s1.orElse(s2).runCollect.map(assert(_)(equalTo(Chunk(1, 2, 3, 4, 5, 6)))) }, testM("orElseEither") { val s1 = ZStream.succeed(1) ++ ZStream.fail("Boom") val s2 = ZStream.succeed(2) - s1.orElseEither(s2).runCollect.map(assert(_)(equalTo(List(Left(1), Right(2))))) + s1.orElseEither(s2).runCollect.map(assert(_)(equalTo(Chunk(Left(1), Right(2))))) }, testM("orElseFail") { val s1 = ZStream.succeed(1) ++ ZStream.fail("Boom") @@ -2028,11 +2150,11 @@ object ZStreamSpec extends ZIOBaseSpec { testM("orElseOptional") { val s1 = ZStream.succeed(1) ++ ZStream.fail(None) val s2 = ZStream.succeed(2) - s1.orElseOptional(s2).runCollect.map(assert(_)(equalTo(List(1, 2)))) + s1.orElseOptional(s2).runCollect.map(assert(_)(equalTo(Chunk(1, 2)))) }, testM("orElseSucceed") { val s1 = ZStream.succeed(1) ++ ZStream.fail("Boom") - s1.orElseSucceed(2).runCollect.map(assert(_)(equalTo(List(1, 2)))) + s1.orElseSucceed(2).runCollect.map(assert(_)(equalTo(Chunk(1, 2)))) }, suite("repeat")( testM("repeat")( @@ -2040,7 +2162,7 @@ object ZStreamSpec extends ZIOBaseSpec { ZStream(1) .repeat(Schedule.recurs(4)) .runCollect - )(equalTo(List(1, 1, 1, 1, 1))) + )(equalTo(Chunk(1, 1, 1, 1, 1))) ), testM("short circuits")( for { @@ -2065,7 +2187,7 @@ object ZStreamSpec extends ZIOBaseSpec { .runCollect )( equalTo( - List( + Chunk( Right(1), Right(1), Left(1), @@ -2122,39 +2244,45 @@ object ZStreamSpec extends ZIOBaseSpec { testM("scheduleWith")( assertM( ZStream("A", "B", "C", "A", "B", "C") - .scheduleWith(Schedule.recurs(2) *> Schedule.fromFunction((_) => "Done"))(_.toLowerCase, identity) + .scheduleWith(Schedule.recurs(2) *> Schedule.fromFunction((_) => "Done"))( + _.toLowerCase, + identity + ) .runCollect - )(equalTo(List("a", "b", "c", "Done", "a", "b", "c", "Done"))) + )(equalTo(Chunk("a", "b", "c", "Done", "a", "b", "c", "Done"))) ), testM("scheduleEither")( assertM( ZStream("A", "B", "C") .scheduleEither(Schedule.recurs(2) *> Schedule.fromFunction((_) => "!")) .runCollect - )(equalTo(List(Right("A"), Right("B"), Right("C"), Left("!")))) + )(equalTo(Chunk(Right("A"), Right("B"), Right("C"), Left("!")))) ) ), suite("scheduleElements")( testM("scheduleElementsWith")( assertM( ZStream("A", "B", "C") - .scheduleElementsWith(Schedule.recurs(0) *> Schedule.fromFunction((_) => 123))(identity, _.toString) + .scheduleElementsWith(Schedule.recurs(0) *> Schedule.fromFunction((_) => 123))( + identity, + _.toString + ) .runCollect - )(equalTo(List("A", "123", "B", "123", "C", "123"))) + )(equalTo(Chunk("A", "123", "B", "123", "C", "123"))) ), testM("scheduleElementsEither")( assertM( ZStream("A", "B", "C") .scheduleElementsEither(Schedule.recurs(0) *> Schedule.fromFunction((_) => 123)) .runCollect - )(equalTo(List(Right("A"), Left(123), Right("B"), Left(123), Right("C"), Left(123)))) + )(equalTo(Chunk(Right("A"), Left(123), Right("B"), Left(123), Right("C"), Left(123)))) ), testM("repeated && assertspaced")( assertM( ZStream("A", "B", "C") .scheduleElements(Schedule.once) .runCollect - )(equalTo(List("A", "A", "B", "B", "C", "C"))) + )(equalTo(Chunk("A", "A", "B", "B", "C", "C"))) ), testM("short circuits in schedule")( assertM( @@ -2162,7 +2290,7 @@ object ZStreamSpec extends ZIOBaseSpec { .scheduleElements(Schedule.once) .take(4) .runCollect - )(equalTo(List("A", "A", "B", "B"))) + )(equalTo(Chunk("A", "A", "B", "B"))) ), testM("short circuits after schedule")( assertM( @@ -2170,7 +2298,7 @@ object ZStreamSpec extends ZIOBaseSpec { .scheduleElements(Schedule.once) .take(3) .runCollect - )(equalTo(List("A", "A", "B"))) + )(equalTo(Chunk("A", "A", "B"))) ) ), testM("some") { @@ -2179,7 +2307,7 @@ object ZStreamSpec extends ZIOBaseSpec { }, testM("someOrElse") { val s1 = ZStream.succeed(Some(1)) ++ ZStream.succeed(None) - s1.someOrElse(-1).runCollect.map(assert(_)(equalTo(List(1, -1)))) + s1.someOrElse(-1).runCollect.map(assert(_)(equalTo(Chunk(1, -1)))) }, testM("someOrFail") { val s1 = ZStream.succeed(Some(1)) ++ ZStream.succeed(None) @@ -2190,7 +2318,9 @@ object ZStreamSpec extends ZIOBaseSpec { for { takeStreamResult <- s.take(n.toLong).runCollect.run takeListResult <- s.runCollect.map(_.take(n)).run - } yield assert(takeListResult.succeeded)(isTrue) implies assert(takeStreamResult)(equalTo(takeListResult)) + } yield assert(takeListResult.succeeded)(isTrue) implies assert(takeStreamResult)( + equalTo(takeListResult) + ) }), testM("take short circuits")( for { @@ -2203,28 +2333,34 @@ object ZStreamSpec extends ZIOBaseSpec { testM("take(0) short circuits")( for { units <- ZStream.never.take(0).runCollect - } yield assert(units)(equalTo(Nil)) + } yield assert(units)(equalTo(Chunk.empty)) ), testM("take(1) short circuits")( for { ints <- (ZStream(1) ++ ZStream.never).take(1).runCollect - } yield assert(ints)(equalTo(List(1))) + } yield assert(ints)(equalTo(Chunk(1))) ) ), testM("takeUntil") { checkM(streamOfBytes, Gen.function(Gen.boolean)) { (s, p) => for { streamTakeUntil <- s.takeUntil(p).runCollect.run - listTakeUntil <- s.runCollect.map(as => as.takeWhile(!p(_)) ++ as.dropWhile(!p(_)).take(1)).run - } yield assert(listTakeUntil.succeeded)(isTrue) implies assert(streamTakeUntil)(equalTo(listTakeUntil)) + chunkTakeUntil <- s.runCollect + .map(as => as.takeWhile(!p(_)) ++ as.dropWhile(!p(_)).take(1)) + .run + } yield assert(chunkTakeUntil.succeeded)(isTrue) implies assert(streamTakeUntil)( + equalTo(chunkTakeUntil) + ) } }, suite("takeWhile")( testM("takeWhile")(checkM(streamOfBytes, Gen.function(Gen.boolean)) { (s, p) => for { streamTakeWhile <- s.takeWhile(p).runCollect.run - listTakeWhile <- s.runCollect.map(_.takeWhile(p)).run - } yield assert(listTakeWhile.succeeded)(isTrue) implies assert(streamTakeWhile)(equalTo(listTakeWhile)) + chunkTakeWhile <- s.runCollect.map(_.takeWhile(p)).run + } yield assert(chunkTakeWhile.succeeded)(isTrue) implies assert(streamTakeWhile)( + equalTo(chunkTakeWhile) + ) }), testM("takeWhile short circuits")( assertM( @@ -2241,11 +2377,11 @@ object ZStreamSpec extends ZIOBaseSpec { ref <- Ref.make(0) res <- ZStream(1, 1).tap[Any, Nothing](a => ref.update(_ + a)).runCollect sum <- ref.get - } yield assert(res)(equalTo(List(1, 1))) && assert(sum)(equalTo(2)) + } yield assert(res)(equalTo(Chunk(1, 1))) && assert(sum)(equalTo(2)) }, testM("laziness on chunks") { assertM(Stream(1, 2, 3).tap(x => IO.when(x == 3)(IO.fail("error"))).either.runCollect)( - equalTo(List(Right(1), Right(2), Left("error"))) + equalTo(Chunk(Right(1), Right(2), Left("error"))) ) } ), @@ -2255,14 +2391,14 @@ object ZStreamSpec extends ZIOBaseSpec { ZStream(1, 2, 3, 4) .throttleEnforce(0, Duration.Infinity)(_ => 0) .runCollect - )(equalTo(List(1, 2, 3, 4))) + )(equalTo(Chunk(1, 2, 3, 4))) }, testM("no bandwidth") { assertM( ZStream(1, 2, 3, 4) .throttleEnforce(0, Duration.Infinity)(_ => 1) .runCollect - )(equalTo(Nil)) + )(equalTo(Chunk.empty)) } ), suite("throttleShape")( @@ -2271,8 +2407,11 @@ object ZStreamSpec extends ZIOBaseSpec { fiber <- Queue .bounded[Int](10) .flatMap { queue => - ZStream.fromQueue(queue).throttleShape(1, 1.second)(_.fold(0)(_ + _).toLong).process.use { - pull => + ZStream + .fromQueue(queue) + .throttleShape(1, 1.second)(_.fold(0)(_ + _).toLong) + .process + .use { pull => for { _ <- queue.offer(1) res1 <- pull @@ -2281,8 +2420,10 @@ object ZStreamSpec extends ZIOBaseSpec { _ <- clock.sleep(4.seconds) _ <- queue.offer(3) res3 <- pull - } yield assert(List(res1, res2, res3))(equalTo(List(Chunk(1), Chunk(2), Chunk(3)))) - } + } yield assert(Chunk(res1, res2, res3))( + equalTo(Chunk(Chunk(1), Chunk(2), Chunk(3))) + ) + } } .fork _ <- TestClock.adjust(8.seconds) @@ -2298,7 +2439,9 @@ object ZStreamSpec extends ZIOBaseSpec { _ <- queue.offer(2) res2 <- pull elapsed <- clock.currentTime(TimeUnit.SECONDS) - } yield assert(elapsed)(equalTo(0L)) && assert(List(res1, res2))(equalTo(List(Chunk(1), Chunk(2)))) + } yield assert(elapsed)(equalTo(0L)) && assert(Chunk(res1, res2))( + equalTo(Chunk(Chunk(1), Chunk(2))) + ) } } }, @@ -2307,8 +2450,11 @@ object ZStreamSpec extends ZIOBaseSpec { fiber <- Queue .bounded[Int](10) .flatMap { queue => - ZStream.fromQueue(queue).throttleShape(1, 1.second, 2)(_.fold(0)(_ + _).toLong).process.use { - pull => + ZStream + .fromQueue(queue) + .throttleShape(1, 1.second, 2)(_.fold(0)(_ + _).toLong) + .process + .use { pull => for { _ <- queue.offer(1) res1 <- pull @@ -2318,8 +2464,10 @@ object ZStreamSpec extends ZIOBaseSpec { _ <- TestClock.adjust(4.seconds) _ <- queue.offer(3) res3 <- pull - } yield assert(List(res1, res2, res3))(equalTo(List(Chunk(1), Chunk(2), Chunk(3)))) - } + } yield assert(Chunk(res1, res2, res3))( + equalTo(Chunk(Chunk(1), Chunk(2), Chunk(3))) + ) + } } .fork test <- fiber.join @@ -2330,7 +2478,7 @@ object ZStreamSpec extends ZIOBaseSpec { ZStream(1, 2, 3, 4) .throttleShape(1, Duration.Infinity)(_ => 0) .runCollect - )(equalTo(List(1, 2, 3, 4))) + )(equalTo(Chunk(1, 2, 3, 4))) } ), suite("debounce")( @@ -2351,7 +2499,7 @@ object ZStreamSpec extends ZIOBaseSpec { _ <- (clock.sleep(2500.millis) *> c.offer).fork _ <- TestClock.adjust(3500.millis) result <- fiber.join - } yield result)(equalTo(List(Chunk(3, 4), Chunk(6, 7)))) + } yield result)(equalTo(Chunk(Chunk(3, 4), Chunk(6, 7)))) } }, testM("should take latest chunk within waitTime") { @@ -2367,7 +2515,7 @@ object ZStreamSpec extends ZIOBaseSpec { _ <- c.offer *> c.offer *> c.offer _ <- TestClock.adjust(1.second) result <- fiber.join - } yield result)(equalTo(List(Chunk(5, 6)))) + } yield result)(equalTo(Chunk(Chunk(5, 6)))) } }, testM("should work properly with parallelization") { @@ -2391,7 +2539,7 @@ object ZStreamSpec extends ZIOBaseSpec { fiber <- ZStream(1, 2, 3).fixed[Any](500.millis).debounce(1.second).runCollect.fork _ <- TestClock.adjust(3.seconds) result <- fiber.join - } yield assert(result)(equalTo(List(3))) + } yield assert(result)(equalTo(Chunk(3))) }, testM("should fail immediately") { val stream = ZStream.fromEffect(IO.fail(None)).debounce(Duration.Infinity) @@ -2406,7 +2554,7 @@ object ZStreamSpec extends ZIOBaseSpec { fiber <- ZStream(1, 2, 3).debounce(1.second).runCollect.fork _ <- TestClock.adjust(1.second) result <- fiber.join - } yield result)(equalTo(List(3))) + } yield result)(equalTo(Chunk(3))) } ), suite("timeout")( @@ -2416,7 +2564,7 @@ object ZStreamSpec extends ZIOBaseSpec { .succeed(1) .timeout(Duration.Infinity) .runCollect - )(equalTo(List(1))) + )(equalTo(Chunk(1))) }, testM("should end stream") { assertM( @@ -2459,7 +2607,7 @@ object ZStreamSpec extends ZIOBaseSpec { .range(0, 5) .timeoutTo(Duration.Infinity)(ZStream.succeed(-1)) .runCollect - )(equalTo(List(0, 1, 2, 3, 4))) + )(equalTo(Chunk(0, 1, 2, 3, 4))) }, testM("should switch stream") { assertWithChunkCoordination(List(Chunk(1), Chunk(2), Chunk(3))) { c => @@ -2478,7 +2626,7 @@ object ZStreamSpec extends ZIOBaseSpec { _ <- c.offer result <- fiber.join } yield result - )(equalTo(List(1, 2, 4))) + )(equalTo(Chunk(1, 2, 4))) } }, testM("should not apply timeout after switch") { @@ -2494,7 +2642,7 @@ object ZStreamSpec extends ZIOBaseSpec { _ <- queue2.offer(4) *> TestClock.adjust(3.second) _ <- queue2.offer(5) *> queue2.shutdown result <- fiber.join - } yield assert(result)(equalTo(List(1, 2, 4, 5))) + } yield assert(result)(equalTo(Chunk(1, 2, 4, 5))) } ), suite("toInputStream")( @@ -2503,7 +2651,9 @@ object ZStreamSpec extends ZIOBaseSpec { val content = chunks.flatMap(_.toList) ZStream.fromChunks(chunks: _*).toInputStream.use[Any, Throwable, TestResult] { is => ZIO.succeedNow( - assert(Iterator.continually(is.read()).takeWhile(_ != -1).map(_.toByte).toList)(equalTo(content)) + assert(Iterator.continually(is.read()).takeWhile(_ != -1).map(_.toByte).toList)( + equalTo(content) + ) ) } } @@ -2561,9 +2711,18 @@ object ZStreamSpec extends ZIOBaseSpec { fails(hasMessage(equalTo("boom"))) ) }, + testM("Be completely lazy") { + assertM( + ZStream + .fail(new Exception("boom")) + .toInputStream + .use(_ => ZIO.succeed("ok")) + )(equalTo("ok")) + }, testM("Preserves errors in the middle") { - val bytes: Seq[Byte] = (1 to 5).map(_.toByte) - val str: ZStream[Any, Throwable, Byte] = ZStream.fromIterable(bytes) ++ ZStream.fail(new Exception("boom")) + val bytes: Seq[Byte] = (1 to 5).map(_.toByte) + val str: ZStream[Any, Throwable, Byte] = + ZStream.fromIterable(bytes) ++ ZStream.fail(new Exception("boom")) assertM( str.toInputStream .use(is => @@ -2577,8 +2736,9 @@ object ZStreamSpec extends ZIOBaseSpec { )(fails(hasMessage(equalTo("boom")))) }, testM("Allows reading something even in case of error") { - val bytes: Seq[Byte] = (1 to 5).map(_.toByte) - val str: ZStream[Any, Throwable, Byte] = ZStream.fromIterable(bytes) ++ ZStream.fail(new Exception("boom")) + val bytes: Seq[Byte] = (1 to 5).map(_.toByte) + val str: ZStream[Any, Throwable, Byte] = + ZStream.fromIterable(bytes) ++ ZStream.fail(new Exception("boom")) assertM( str.toInputStream.use(is => Task { @@ -2602,7 +2762,7 @@ object ZStreamSpec extends ZIOBaseSpec { .take(n.toLong) .runCollect .toManaged_ - } yield assert(out)(equalTo((1 to n).toList))).use(ZIO.succeed(_)) + } yield assert(out)(equalTo(Chunk.fromIterable(1 to n)))).use(ZIO.succeed(_)) } @@ TestAspect.jvmOnly, // Until #3360 is solved suite("toQueue")( testM("toQueue")(checkM(Gen.chunkOfBounded(0, 3)(Gen.anyInt)) { (c: Chunk[Int]) => @@ -2625,9 +2785,11 @@ object ZStreamSpec extends ZIOBaseSpec { ), suite("zipWith")( testM("zip doesn't pull too much when one of the streams is done") { - val l = ZStream.fromChunks(Chunk(1, 2), Chunk(3, 4), Chunk(5)) ++ ZStream.fail("Nothing to see here") + val l = ZStream.fromChunks(Chunk(1, 2), Chunk(3, 4), Chunk(5)) ++ ZStream.fail( + "Nothing to see here" + ) val r = ZStream.fromChunks(Chunk("a", "b"), Chunk("c")) - assertM(l.zip(r).runCollect)(equalTo(List((1, "a"), (2, "b"), (3, "c")))) + assertM(l.zip(r).runCollect)(equalTo(Chunk((1, "a"), (2, "b"), (3, "c")))) }, testM("zip equivalence with Chunk#zipWith") { checkM( @@ -2635,7 +2797,9 @@ object ZStreamSpec extends ZIOBaseSpec { tinyListOf(Gen.chunkOf(Gen.anyInt)) ) { (l, r) => val expected = Chunk.fromIterable(l).flatten.zip(Chunk.fromIterable(r).flatten) - assertM(ZStream.fromChunks(l: _*).zip(ZStream.fromChunks(r: _*)).runCollect)(equalTo(expected.toList)) + assertM(ZStream.fromChunks(l: _*).zip(ZStream.fromChunks(r: _*)).runCollect)( + equalTo(expected) + ) } }, testM("zipWith prioritizes failure") { @@ -2669,7 +2833,7 @@ object ZStreamSpec extends ZIOBaseSpec { .map(Option(_)) .zipAll(ZStream.fromChunks(r: _*).map(Option(_)))(None, None) .runCollect - )(equalTo(expected.toList)) + )(equalTo(expected)) } }, testM("zipAllWith prioritizes failure") { @@ -2687,29 +2851,40 @@ object ZStreamSpec extends ZIOBaseSpec { res2 <- (s.runCollect.map(_.zipWithIndex.map(t => (t._1, t._2.toLong)))) } yield assert(res1)(equalTo(res2)) }), - testM("zipWithLatest") { - import zio.test.environment.TestClock - - val s1 = ZStream.iterate(0)(_ + 1).fixed(100.millis) - val s2 = ZStream.iterate(0)(_ + 1).fixed(70.millis) - val s3 = s1.zipWithLatest(s2)((_, _)) + suite("zipWithLatest")( + testM("succeed") { + val s1 = ZStream.iterate(0)(_ + 1).fixed(100.millis) + val s2 = ZStream.iterate(0)(_ + 1).fixed(70.millis) + val s3 = s1.zipWithLatest(s2)((_, _)) - for { - fiber <- s3.take(4).runCollect.fork - _ <- TestClock.setTime(210.milliseconds) - value <- fiber.join - } yield assert(value)(equalTo(List(0 -> 0, 0 -> 1, 1 -> 1, 1 -> 2))) - }, + for { + fiber <- s3.take(4).runCollect.fork + _ <- TestClock.setTime(210.milliseconds) + value <- fiber.join + } yield assert(value)(equalTo(Chunk(0 -> 0, 0 -> 1, 1 -> 1, 1 -> 2))) + }, + testM("handle empty pulls properly") { + assertM( + ZStream + .unfold(0)(n => Some((if (n < 3) Chunk.empty else Chunk.single(2), n + 1))) + .flattenChunks + .forever + .zipWithLatest(ZStream(1).forever)((_, x) => x) + .take(3) + .runCollect + )(equalTo(Chunk(1, 1, 1))) + } + ), suite("zipWithNext")( testM("should zip with next element for a single chunk") { for { result <- ZStream(1, 2, 3).zipWithNext.runCollect - } yield assert(result)(equalTo(List(1 -> Some(2), 2 -> Some(3), 3 -> None))) + } yield assert(result)(equalTo(Chunk(1 -> Some(2), 2 -> Some(3), 3 -> None))) }, testM("should work with multiple chunks") { for { result <- ZStream.fromChunks(Chunk(1), Chunk(2), Chunk(3)).zipWithNext.runCollect - } yield assert(result)(equalTo(List(1 -> Some(2), 2 -> Some(3), 3 -> None))) + } yield assert(result)(equalTo(Chunk(1 -> Some(2), 2 -> Some(3), 3 -> None))) }, testM("should play well with empty streams") { assertM(ZStream.empty.zipWithNext.runCollect)(isEmpty) @@ -2728,12 +2903,12 @@ object ZStreamSpec extends ZIOBaseSpec { testM("should zip with previous element for a single chunk") { for { result <- ZStream(1, 2, 3).zipWithPrevious.runCollect - } yield assert(result)(equalTo(List(None -> 1, Some(1) -> 2, Some(2) -> 3))) + } yield assert(result)(equalTo(Chunk(None -> 1, Some(1) -> 2, Some(2) -> 3))) }, testM("should work with multiple chunks") { for { result <- ZStream.fromChunks(Chunk(1), Chunk(2), Chunk(3)).zipWithPrevious.runCollect - } yield assert(result)(equalTo(List(None -> 1, Some(1) -> 2, Some(2) -> 3))) + } yield assert(result)(equalTo(Chunk(None -> 1, Some(1) -> 2, Some(2) -> 3))) }, testM("should play well with empty streams") { assertM(ZStream.empty.zipWithPrevious.runCollect)(isEmpty) @@ -2751,9 +2926,10 @@ object ZStreamSpec extends ZIOBaseSpec { suite("zipWithPreviousAndNext")( testM("succeed") { for { - result0 <- ZStream(1, 2, 3).zipWithPreviousAndNext.runCollect - result = List((None, 1, Some(2)), (Some(1), 2, Some(3)), (Some(2), 3, None)) - } yield assert(result0)(equalTo(result)) + result <- ZStream(1, 2, 3).zipWithPreviousAndNext.runCollect + } yield assert(result)( + equalTo(Chunk((None, 1, Some(2)), (Some(1), 2, Some(3)), (Some(2), 3, None))) + ) }, testM("should output same values as zipping with both previous and next element") { checkM(tinyListOf(Gen.chunkOf(Gen.anyInt))) { chunks => @@ -2762,7 +2938,11 @@ object ZStreamSpec extends ZIOBaseSpec { result0 <- stream.zipWithPreviousAndNext.runCollect previous = ZStream(None) ++ stream.map(Some(_)) next = stream.drop(1).map(Some(_)) ++ ZStream(None) - result1 <- previous.zip(stream).zip(next).map { case ((p, c), n) => (p, c, n) }.runCollect + result1 <- previous + .zip(stream) + .zip(next) + .map { case ((p, c), n) => (p, c, n) } + .runCollect } yield assert(result0)(equalTo(result1)) } } @@ -2777,7 +2957,11 @@ object ZStreamSpec extends ZIOBaseSpec { suite("accessM")( testM("accessM") { for { - result <- ZStream.accessM[String](ZIO.succeedNow).provide("test").runCollect.map(_.head) + result <- ZStream + .accessM[String](ZIO.succeedNow) + .provide("test") + .runCollect + .map(_.head) } yield assert(result)(equalTo("test")) }, testM("accessM fails") { @@ -2789,32 +2973,42 @@ object ZStreamSpec extends ZIOBaseSpec { suite("accessStream")( testM("accessStream") { for { - result <- ZStream.accessStream[String](ZStream.succeed(_)).provide("test").runCollect.map(_.head) + result <- ZStream + .accessStream[String](ZStream.succeed(_)) + .provide("test") + .runCollect + .map(_.head) } yield assert(result)(equalTo("test")) }, testM("accessStream fails") { for { - result <- ZStream.accessStream[Int](_ => ZStream.fail("fail")).provide(0).runCollect.run + result <- ZStream + .accessStream[Int](_ => ZStream.fail("fail")) + .provide(0) + .runCollect + .run } yield assert(result)(fails(equalTo("fail"))) } ), testM("chunkN") { - checkM(tinyListOf(Gen.chunkOf(Gen.anyInt)) <*> (Gen.int(1, 100))) { - case (list, n) => - val expected = list.flatten.grouped(n).toList + checkM(tinyChunkOf(Gen.chunkOf(Gen.anyInt)) <*> (Gen.int(1, 100))) { + case (chunk, n) => + val expected = Chunk.fromIterable(chunk.flatten.grouped(n).toList) assertM( ZStream - .fromChunks(list: _*) + .fromChunks(chunk: _*) .chunkN(n) - .mapChunks(ch => Chunk(ch.toList)) + .mapChunks(ch => Chunk(ch)) .runCollect )(equalTo(expected)) } }, testM("concatAll") { checkM(tinyListOf(Gen.chunkOf(Gen.anyInt))) { chunks => - assertM(ZStream.concatAll(Chunk.fromIterable(chunks.map(ZStream.fromChunk(_)))).runCollect)( - equalTo(Chunk.fromIterable(chunks).flatten.toList) + assertM( + ZStream.concatAll(Chunk.fromIterable(chunks.map(ZStream.fromChunk(_)))).runCollect + )( + equalTo(Chunk.fromIterable(chunks).flatten) ) } }, @@ -2843,14 +3037,14 @@ object ZStreamSpec extends ZIOBaseSpec { } ), testM("fromChunk") { - checkM(Gen.small(Gen.chunkOfN(_)(Gen.anyInt)))(c => - assertM(ZStream.fromChunk(c).runCollect)(equalTo(c.toList)) - ) + checkM(Gen.small(Gen.chunkOfN(_)(Gen.anyInt)))(c => assertM(ZStream.fromChunk(c).runCollect)(equalTo(c))) }, suite("fromChunks")( testM("fromChunks") { checkM(tinyListOf(Gen.chunkOf(Gen.anyInt))) { cs => - assertM(ZStream.fromChunks(cs: _*).runCollect)(equalTo(Chunk.fromIterable(cs).flatten.toList)) + assertM(ZStream.fromChunks(cs: _*).runCollect)( + equalTo(Chunk.fromIterable(cs).flatten) + ) } }, testM("discards empty chunks") { @@ -2862,7 +3056,7 @@ object ZStreamSpec extends ZIOBaseSpec { suite("fromEffectOption")( testM("emit one element with success") { val fa: ZIO[Any, Option[Int], Int] = ZIO.succeed(5) - assertM(ZStream.fromEffectOption(fa).runCollect)(equalTo(List(5))) + assertM(ZStream.fromEffectOption(fa).runCollect)(equalTo(Chunk(5))) }, testM("emit one element with failure") { val fa: ZIO[Any, Option[Int], Int] = ZIO.fail(Some(5)) @@ -2870,7 +3064,7 @@ object ZStreamSpec extends ZIOBaseSpec { } @@ zioTag(errors), testM("do not emit any element") { val fa: ZIO[Any, Option[Int], Int] = ZIO.fail(None) - assertM(ZStream.fromEffectOption(fa).runCollect)(equalTo(List())) + assertM(ZStream.fromEffectOption(fa).runCollect)(equalTo(Chunk())) } ), suite("fromInputStream")( @@ -2881,24 +3075,24 @@ object ZStreamSpec extends ZIOBaseSpec { ZStream.fromInputStream(is, chunkSize).runCollect map { bytes => assert(bytes.toArray)(equalTo(data)) } }, testM("example 2") { - checkM(Gen.small(Gen.listOfN(_)(Gen.anyByte)), Gen.int(1, 10)) { (bytes, chunkSize) => + checkM(Gen.small(Gen.chunkOfN(_)(Gen.anyByte)), Gen.int(1, 10)) { (bytes, chunkSize) => val is = new ByteArrayInputStream(bytes.toArray) ZStream.fromInputStream(is, chunkSize).runCollect.map(assert(_)(equalTo(bytes))) } } ), - testM("fromIterable")(checkM(Gen.small(Gen.listOfN(_)(Gen.anyInt))) { l => + testM("fromIterable")(checkM(Gen.small(Gen.chunkOfN(_)(Gen.anyInt))) { l => def lazyL = l assertM(ZStream.fromIterable(lazyL).runCollect)(equalTo(l)) }), - testM("fromIterableM")(checkM(Gen.small(Gen.listOfN(_)(Gen.anyInt))) { l => + testM("fromIterableM")(checkM(Gen.small(Gen.chunkOfN(_)(Gen.anyInt))) { l => assertM(ZStream.fromIterableM(UIO.effectTotal(l)).runCollect)(equalTo(l)) }), - testM("fromIterator")(checkM(Gen.small(Gen.listOfN(_)(Gen.anyInt))) { l => + testM("fromIterator")(checkM(Gen.small(Gen.chunkOfN(_)(Gen.anyInt))) { l => def lazyIt = l.iterator assertM(ZStream.fromIterator(lazyIt).runCollect)(equalTo(l)) }), - testM("fromIteratorTotal")(checkM(Gen.small(Gen.listOfN(_)(Gen.anyInt))) { l => + testM("fromIteratorTotal")(checkM(Gen.small(Gen.chunkOfN(_)(Gen.anyInt))) { l => def lazyIt = l.iterator assertM(ZStream.fromIteratorTotal(lazyIt).runCollect)(equalTo(l)) }), @@ -2907,7 +3101,9 @@ object ZStreamSpec extends ZIOBaseSpec { for { ref <- Ref.make(false) pulls <- ZStream - .fromIteratorManaged(Managed.make(UIO.succeedNow(List(1, 2).iterator))(_ => ref.set(true))) + .fromIteratorManaged( + Managed.make(UIO.succeedNow(List(1, 2).iterator))(_ => ref.set(true)) + ) .process .use(nPulls(_, 4)) fin <- ref.get @@ -2925,14 +3121,18 @@ object ZStreamSpec extends ZIOBaseSpec { .process .use(nPulls(_, 3)) fin <- ref.get - } yield assert(fin)(isFalse) && assert(pulls)(equalTo(List(Left(Some(ex)), Left(None), Left(None)))) + } yield assert(fin)(isFalse) && assert(pulls)( + equalTo(List(Left(Some(ex)), Left(None), Left(None))) + ) }, testM("is safe to pull again after inner failure") { val ex = new Exception("Ouch") for { ref <- Ref.make(false) pulls <- ZStream - .fromIteratorManaged(Managed.make(UIO.succeedNow(List(1, 2).iterator))(_ => ref.set(true))) + .fromIteratorManaged( + Managed.make(UIO.succeedNow(List(1, 2).iterator))(_ => ref.set(true)) + ) .flatMap(n => ZStream.succeed((n * 2).toString) ++ ZStream.fail(ex) ++ ZStream.succeed( (n * 3).toString @@ -2973,7 +3173,7 @@ object ZStreamSpec extends ZIOBaseSpec { _ <- TestClock.adjust(62.seconds) value <- fiber.join } yield value - val expected = List(1.seconds, 2.seconds, 4.seconds, 8.seconds, 16.seconds, 32.seconds) + val expected = Chunk(1.seconds, 2.seconds, 4.seconds, 8.seconds, 16.seconds, 32.seconds) assertM(zio)(equalTo(expected)) }, testM("fromQueue") { @@ -2988,7 +3188,7 @@ object ZStreamSpec extends ZIOBaseSpec { .fork _ <- c.offer result <- fiber.join - } yield result)(equalTo(List(1, 2))) + } yield result)(equalTo(Chunk(1, 2))) } }, testM("fromTQueue") { @@ -2999,13 +3199,15 @@ object ZStreamSpec extends ZIOBaseSpec { first <- ZStream.fromQueue(queue).take(3).runCollect _ <- tqueue.offerAll(List(4, 5)).commit second <- ZStream.fromQueue(queue).take(2).runCollect - } yield assert(first)(equalTo(List(1, 2, 3).map(Take.single))) && - assert(second)(equalTo(List(4, 5).map(Take.single))) + } yield assert(first)(equalTo(Chunk(1, 2, 3).map(Take.single))) && + assert(second)(equalTo(Chunk(4, 5).map(Take.single))) } } } @@ flaky, testM("iterate")( - assertM(ZStream.iterate(1)(_ + 1).take(10).runCollect)(equalTo((1 to 10).toList)) + assertM(ZStream.iterate(1)(_ + 1).take(10).runCollect)( + equalTo(Chunk.fromIterable(1 to 10)) + ) ), testM("paginate") { val s = (0, List(1, 2, 3)) @@ -3016,7 +3218,7 @@ object ZStreamSpec extends ZIOBaseSpec { case (x, x0 :: xs) => x -> Some(x0 -> xs) } .runCollect - .map(assert(_)(equalTo(List(0, 1, 2, 3)))) + .map(assert(_)(equalTo(Chunk(0, 1, 2, 3)))) }, testM("paginateM") { val s = (0, List(1, 2, 3)) @@ -3028,10 +3230,10 @@ object ZStreamSpec extends ZIOBaseSpec { case (x, x0 :: xs) => ZIO.succeed(x -> Some(x0 -> xs)) } .runCollect - )(equalTo(List(0, 1, 2, 3))) + )(equalTo(Chunk(0, 1, 2, 3))) }, testM("range") { - assertM(ZStream.range(0, 10).runCollect)(equalTo(Range(0, 10).toList)) + assertM(ZStream.range(0, 10).runCollect)(equalTo(Chunk.fromIterable(Range(0, 10)))) }, testM("repeatEffect")( assertM( @@ -3039,7 +3241,7 @@ object ZStreamSpec extends ZIOBaseSpec { .repeatEffect(IO.succeed(1)) .take(2) .runCollect - )(equalTo(List(1, 1))) + )(equalTo(Chunk(1, 1))) ), suite("repeatEffectOption")( testM("emit elements")( @@ -3048,7 +3250,7 @@ object ZStreamSpec extends ZIOBaseSpec { .repeatEffectOption(IO.succeed(1)) .take(2) .runCollect - )(equalTo(List(1, 1))) + )(equalTo(Chunk(1, 1))) ), testM("emit elements until pull fails with None")( for { @@ -3061,8 +3263,17 @@ object ZStreamSpec extends ZIOBaseSpec { .repeatEffectOption(fa) .take(10) .runCollect - } yield assert(res)(equalTo(List(1, 2, 3, 4))) - ) + } yield assert(res)(equalTo(Chunk(1, 2, 3, 4))) + ), + testM("stops evaluating the effect once it fails with None") { + for { + ref <- Ref.make(0) + _ <- ZStream.repeatEffectOption(ref.updateAndGet(_ + 1) *> ZIO.fail(None)).process.use { pull => + pull.ignore *> pull.ignore + } + result <- ref.get + } yield assert(result)(equalTo(1)) + } ), suite("repeatEffectWith")( testM("succeed")( @@ -3084,7 +3295,7 @@ object ZStreamSpec extends ZIOBaseSpec { effect = ref.getAndUpdate(_ + 1).filterOrFail(_ <= length + 1)(()) schedule = Schedule.identity[Int].whileOutput(_ <= length) result <- ZStream.repeatEffectWith(effect, schedule).runCollect - } yield assert(result)(equalTo((0 to length).toList)) + } yield assert(result)(equalTo(Chunk.fromIterable(0 to length))) }) ), testM("unfold") { @@ -3095,7 +3306,7 @@ object ZStreamSpec extends ZIOBaseSpec { else None } .runCollect - )(equalTo((0 to 9).toList)) + )(equalTo(Chunk.fromIterable(0 to 9))) }, testM("unfoldM") { assertM( @@ -3105,7 +3316,7 @@ object ZStreamSpec extends ZIOBaseSpec { else IO.succeed(None) } .runCollect - )(equalTo((0 to 9).toList)) + )(equalTo(Chunk.fromIterable(0 to 9))) } ) ) @@ TestAspect.timed @@ -3119,7 +3330,9 @@ object ZStreamSpec extends ZIOBaseSpec { def assertWithChunkCoordination[A]( chunks: List[Chunk[A]] - )(assertion: ChunkCoordination[A] => ZIO[Clock with TestClock, Nothing, TestResult]) = + )( + assertion: ChunkCoordination[A] => ZIO[Clock with TestClock, Nothing, TestResult] + ) = for { q <- Queue.unbounded[Exit[Option[Nothing], Chunk[A]]] ps <- Queue.unbounded[Unit] diff --git a/streams-tests/shared/src/test/scala/zio/stream/ZTransducerSpec.scala b/streams-tests/shared/src/test/scala/zio/stream/ZTransducerSpec.scala index e842b05f2a98..99b1df31cf53 100644 --- a/streams-tests/shared/src/test/scala/zio/stream/ZTransducerSpec.scala +++ b/streams-tests/shared/src/test/scala/zio/stream/ZTransducerSpec.scala @@ -1,8 +1,9 @@ package zio.stream -import ZStreamGen._ +import scala.io.Source import zio._ +import zio.random.Random import zio.test.Assertion._ import zio.test._ @@ -11,7 +12,7 @@ object ZTransducerSpec extends ZIOBaseSpec { val initErrorParser = ZTransducer.fromEffect(IO.fail("Ouch")) - def run[R, E, I, O](parser: ZTransducer[R, E, I, O], input: List[Chunk[I]]): ZIO[R, E, List[O]] = + def run[R, E, I, O](parser: ZTransducer[R, E, I, O], input: List[Chunk[I]]): ZIO[R, E, Chunk[O]] = ZStream.fromChunks(input: _*).transduce(parser).runCollect def spec = suite("ZTransducerSpec")( @@ -19,7 +20,7 @@ object ZTransducerSpec extends ZIOBaseSpec { suite("contramap")( testM("happy path") { val parser = ZTransducer.identity[Int].contramap[String](_.toInt) - assertM(run(parser, List(Chunk("1"))))(equalTo(List(1))) + assertM(run(parser, List(Chunk("1"))))(equalTo(Chunk(1))) }, testM("error") { val parser = initErrorParser.contramap[String](_.toInt) @@ -29,7 +30,7 @@ object ZTransducerSpec extends ZIOBaseSpec { suite("contramapM")( testM("happy path") { val parser = ZTransducer.identity[Int].contramapM[Any, Unit, String](s => UIO.succeed(s.toInt)) - assertM(run(parser, List(Chunk("1"))))(equalTo(List(1))) + assertM(run(parser, List(Chunk("1"))))(equalTo(Chunk(1))) }, testM("error") { val parser = initErrorParser.contramapM[Any, String, String](s => UIO.succeed(s.toInt)) @@ -39,7 +40,7 @@ object ZTransducerSpec extends ZIOBaseSpec { suite("map")( testM("happy path") { val parser = ZTransducer.identity[Int].map(_.toString) - assertM(run(parser, List(Chunk(1))))(equalTo(List("1"))) + assertM(run(parser, List(Chunk(1))))(equalTo(Chunk("1"))) }, testM("error") { val parser = initErrorParser.map(_.toString) @@ -55,38 +56,23 @@ object ZTransducerSpec extends ZIOBaseSpec { suite("mapM")( testM("happy path") { val parser = ZTransducer.identity[Int].mapM[Any, Unit, String](n => UIO.succeed(n.toString)) - assertM(run(parser, List(Chunk(1))))(equalTo(List("1"))) + assertM(run(parser, List(Chunk(1))))(equalTo(Chunk("1"))) }, testM("error") { val parser = initErrorParser.mapM[Any, String, String](n => UIO.succeed(n.toString)) assertM(run(parser, List(Chunk(1))).either)(isLeft(equalTo("Ouch"))) } @@ zioTag(errors) - ), - suite("zipWith")( - testM("coherence with List#zip") { - val t1: ZTransducer[Any, Nothing, Int, List[Int]] = Transducer.collectAllN(2) - val t2: ZTransducer[Any, Nothing, Int, List[Int]] = Transducer.collectAllN(3) - def f(l: List[Int], r: List[Int]): List[Int] = l ++ r - val t3 = t1.zipWith(t2)(f) - checkM(tinyListOf(Gen.chunkOf(Gen.anyInt))) { chunks => - for { - lefts <- ZStream.fromChunks(chunks: _*).transduce(t1).runCollect - rights <- ZStream.fromChunks(chunks: _*).transduce(t2).runCollect - zipped <- ZStream.fromChunks(chunks: _*).transduce(t3).runCollect - } yield assert(zipped)(equalTo(lefts.zip(rights).map(e => f(e._1, e._2)))) - } - } ) ), suite("Constructors")( suite("collectAllN")( testM("happy path") { val parser = ZTransducer.collectAllN[Int](3) - assertM(run(parser, List(Chunk(1, 2, 3, 4))))(equalTo(List(List(1, 2, 3), List(4)))) + assertM(run(parser, List(Chunk(1, 2, 3, 4))))(equalTo(Chunk(List(1, 2, 3), List(4)))) }, testM("empty list") { val parser = ZTransducer.collectAllN[Int](0) - assertM(run(parser, List()))(equalTo(List(List()))) + assertM(run(parser, List()))(equalTo(Chunk(List()))) } ), suite("collectAllToMapN")( @@ -96,7 +82,7 @@ object ZTransducerSpec extends ZIOBaseSpec { ZTransducer.collectAllToMapN[Int, Int](2)(_ % 3)(_ + _), List(Chunk(0, 1, 2)) ) - )(equalTo(List(Map(0 -> 0, 1 -> 1), Map(2 -> 2)))) + )(equalTo(Chunk(Map(0 -> 0, 1 -> 1), Map(2 -> 2)))) ), testM("keep collecting as long as map size does not exceed the limit")( assertM( @@ -108,19 +94,19 @@ object ZTransducerSpec extends ZIOBaseSpec { Chunk(6, 7, 8, 9) ) ) - )(equalTo(List(Map[Int, Int](0 -> 18, 1 -> 12, 2 -> 15)))) + )(equalTo(Chunk(Map[Int, Int](0 -> 18, 1 -> 12, 2 -> 15)))) ) ), testM("collectAllToSetN")( assertM( run(ZTransducer.collectAllToSetN[Int](3), List(Chunk(1, 2, 1), Chunk(2, 3, 3, 4))) - )(equalTo(List(Set(1, 2, 3), Set(4)))) + )(equalTo(Chunk(Set(1, 2, 3), Set(4)))) ), testM("collectAllWhile") { val parser = ZTransducer.collectAllWhile[Int](_ < 5) val input = List(Chunk(3, 4, 5, 6, 7, 2), Chunk.empty, Chunk(3, 4, 5, 6, 5, 4, 3, 2), Chunk.empty) val result = run(parser, input) - assertM(result)(equalTo(List(List(3, 4), List(2, 3, 4), List(4, 3, 2)))) + assertM(result)(equalTo(Chunk(List(3, 4), List(2, 3, 4), List(4, 3, 2)))) }, suite("fold")( testM("empty")( @@ -128,7 +114,7 @@ object ZTransducerSpec extends ZIOBaseSpec { ZStream.empty .aggregate(ZTransducer.fold[Int, Int](0)(_ => true)(_ + _)) .runCollect - )(equalTo(List(0))) + )(equalTo(Chunk(0))) ), testM("short circuits") { val empty: ZStream[Any, Nothing, Int] = ZStream.empty @@ -147,9 +133,9 @@ object ZTransducerSpec extends ZIOBaseSpec { result <- effects.get } yield (exit, result)).run - (assertM(run(empty))(succeeds(equalTo((List(0), Nil)))) <*> - assertM(run(single))(succeeds(equalTo((List(30), List(1))))) <*> - assertM(run(double))(succeeds(equalTo((List(30), List(2, 1))))) <*> + (assertM(run(empty))(succeeds(equalTo((Chunk(0), Nil)))) <*> + assertM(run(single))(succeeds(equalTo((Chunk(30), List(1))))) <*> + assertM(run(double))(succeeds(equalTo((Chunk(30), List(2, 1))))) <*> assertM(run(failed))(fails(equalTo("Ouch")))).map { case (((r1, r2), r3), r4) => r1 && r2 && r3 && r4 } @@ -163,7 +149,7 @@ object ZTransducerSpec extends ZIOBaseSpec { ZTransducer.foldM(0)(_ => true)((x, y: Int) => ZIO.succeed(x + y)) ) .runCollect - )(equalTo(List(0))) + )(equalTo(Chunk(0))) ), testM("short circuits") { val empty: ZStream[Any, Nothing, Int] = ZStream.empty @@ -182,9 +168,9 @@ object ZTransducerSpec extends ZIOBaseSpec { result <- effects.get } yield exit -> result).run - (assertM(run(empty))(succeeds(equalTo((List(0), Nil)))) <*> - assertM(run(single))(succeeds(equalTo((List(30), List(1))))) <*> - assertM(run(double))(succeeds(equalTo((List(30), List(2, 1))))) <*> + (assertM(run(empty))(succeeds(equalTo((Chunk(0), Nil)))) <*> + assertM(run(single))(succeeds(equalTo((Chunk(30), List(1))))) <*> + assertM(run(double))(succeeds(equalTo((Chunk(30), List(2, 1))))) <*> assertM(run(failed))(fails(equalTo("Ouch")))).map { case (((r1, r2), r3), r4) => r1 && r2 && r3 && r4 } @@ -198,7 +184,7 @@ object ZTransducerSpec extends ZIOBaseSpec { ZTransducer.foldWeighted(List[Long]())((_, x: Long) => x * 2, 12)((acc, el) => el :: acc).map(_.reverse) ) .runCollect - )(equalTo(List(List(1L, 5L), List(2L, 3L)))) + )(equalTo(Chunk(List(1L, 5L), List(2L, 3L)))) ), suite("foldWeightedDecompose")( testM("foldWeightedDecompose")( @@ -216,7 +202,7 @@ object ZTransducerSpec extends ZIOBaseSpec { .map(_.reverse) ) .runCollect - )(equalTo(List(List(1, 3), List(1, 1, 1)))) + )(equalTo(Chunk(List(1, 3), List(1, 1, 1)))) ), testM("empty")( assertM( @@ -225,7 +211,7 @@ object ZTransducerSpec extends ZIOBaseSpec { ZTransducer.foldWeightedDecompose[Int, Int](0)((_, x) => x.toLong, 1000, Chunk.single(_))(_ + _) ) .runCollect - )(equalTo(List(0))) + )(equalTo(Chunk(0))) ) ), testM("foldWeightedM")( @@ -239,7 +225,7 @@ object ZTransducerSpec extends ZIOBaseSpec { .map(_.reverse) ) .runCollect - )(equalTo(List(List(1L, 5L), List(2L, 3L)))) + )(equalTo(Chunk(List(1L, 5L), List(2L, 3L)))) ), suite("foldWeightedDecomposeM")( testM("foldWeightedDecomposeM")( @@ -255,7 +241,7 @@ object ZTransducerSpec extends ZIOBaseSpec { .map(_.reverse) ) .runCollect - )(equalTo(List(List(1, 3), List(1, 1, 1)))) + )(equalTo(Chunk(List(1, 3), List(1, 1, 1)))) ), testM("empty")( assertM( @@ -268,7 +254,7 @@ object ZTransducerSpec extends ZIOBaseSpec { )((x, y) => ZIO.succeed(x + y)) ) .runCollect - )(equalTo(List(0))) + )(equalTo(Chunk(0))) ) ), testM("foldUntil")( @@ -276,14 +262,14 @@ object ZTransducerSpec extends ZIOBaseSpec { ZStream[Long](1, 1, 1, 1, 1, 1) .aggregate(ZTransducer.foldUntil(0L, 3)(_ + _)) .runCollect - )(equalTo(List(3L, 3L))) + )(equalTo(Chunk(3L, 3L))) ), testM("foldUntilM")( assertM( ZStream[Long](1, 1, 1, 1, 1, 1) .aggregate(ZTransducer.foldUntilM(0L, 3)((s, a) => UIO.succeedNow(s + a))) .runCollect - )(equalTo(List(3L, 3L))) + )(equalTo(Chunk(3L, 3L))) ) ), testM("dropWhile")( @@ -291,7 +277,7 @@ object ZTransducerSpec extends ZIOBaseSpec { ZStream(1, 2, 3, 4, 5, 1, 2, 3, 4, 5) .aggregate(ZTransducer.dropWhile(_ < 3)) .runCollect - )(equalTo(List(3, 4, 5, 1, 2, 3, 4, 5))) + )(equalTo(Chunk(3, 4, 5, 1, 2, 3, 4, 5))) ), suite("dropWhileM")( testM("happy path")( @@ -299,7 +285,7 @@ object ZTransducerSpec extends ZIOBaseSpec { ZStream(1, 2, 3, 4, 5, 1, 2, 3, 4, 5) .aggregate(ZTransducer.dropWhileM(x => UIO(x < 3))) .runCollect - )(equalTo(List(3, 4, 5, 1, 2, 3, 4, 5))) + )(equalTo(Chunk(3, 4, 5, 1, 2, 3, 4, 5))) ) // testM("error")( // assertM { @@ -307,7 +293,7 @@ object ZTransducerSpec extends ZIOBaseSpec { // .aggregate(ZTransducer.dropWhileM(x => UIO(x < 3))) // .either // .runCollect - // }(equalTo(List(Right(3),Left("Aie"),Right(5),Right(1),Right(2),Right(3),Right(4),Right(5)))) + // }(equalTo(Chunk(Right(3),Left("Aie"),Right(5),Right(1),Right(2),Right(3),Right(4),Right(5)))) // ) ), testM("fromFunction")( @@ -315,14 +301,14 @@ object ZTransducerSpec extends ZIOBaseSpec { ZStream(1, 2, 3, 4, 5) .aggregate(ZTransducer.fromFunction[Int, String](_.toString)) .runCollect - )(equalTo(List("1", "2", "3", "4", "5"))) + )(equalTo(Chunk("1", "2", "3", "4", "5"))) ), testM("fromFunctionM")( assertM( ZStream("1", "2", "3", "4", "5") .transduce(ZTransducer.fromFunctionM[Any, Throwable, String, Int](s => Task(s.toInt))) .runCollect - )(equalTo(List(1, 2, 3, 4, 5))) + )(equalTo(Chunk(1, 2, 3, 4, 5))) ), suite("splitLines")( testM("preserves data")( @@ -341,105 +327,66 @@ object ZTransducerSpec extends ZIOBaseSpec { testM("preserves data in chunks") { checkM(weirdStringGenForSplitLines) { xs => val data = Chunk.fromIterable(xs.sliding(2, 2).toList.map(_.mkString("\n"))) - val ys = xs.headOption.map(_ :: xs.drop(1).sliding(2, 2).toList.map(_.mkString)).getOrElse(Nil) - - ZTransducer.splitLines.push.use { push => - for { - result <- push(Some(data)) - leftover <- push(None) - } yield assert((result ++ leftover).toArray[String].toList)(equalTo(ys)) - - } + testSplitLines(Seq(data)) } }, testM("handles leftovers") { - ZTransducer.splitLines.push.use { push => - for { - result <- push(Some(Chunk("abc\nbc"))) - leftover <- push(None) - } yield assert(result.toArray[String].mkString("\n"))(equalTo("abc")) && assert( - leftover.toArray[String].mkString - )(equalTo("bc")) - } + testSplitLines(Seq(Chunk("abc\nbc"))) }, testM("handles leftovers 2") { - assertM( - ZStream - .fromChunks(Chunk("aa", "bb"), Chunk("\nbbc\n", "ddb", "bd"), Chunk("abc", "\n"), Chunk("abc")) - .transduce(ZTransducer.splitLines) - .runCollect - )(equalTo(List("aabb", "bbc", "ddbbdabc", "abc"))) + testSplitLines(Seq(Chunk("aa", "bb"), Chunk("\nbbc\n", "ddb", "bd"), Chunk("abc", "\n"), Chunk("abc"))) }, testM("aggregates chunks") { - ZTransducer.splitLines.push.use { push => - for { - part1 <- push(Some(Chunk("abc", "\n", "bc", "\n", "bcd", "bcd"))) - part2 <- push(None) - } yield assert(part1 ++ part2)(equalTo(Chunk("abc", "bc", "bcdbcd"))) - } + testSplitLines(Seq(Chunk("abc", "\n", "bc", "\n", "bcd", "bcd"))) }, testM("single newline edgecase") { - ZTransducer.splitLines.push.use { push => - for { - part1 <- push(Some(Chunk("\n"))) - part2 <- push(None) - } yield assert(part1 ++ part2)(equalTo(Chunk(""))) - } + testSplitLines(Seq(Chunk("\n"))) }, testM("no newlines in data") { - ZTransducer.splitLines.push.use { push => - for { - part1 <- push(Some(Chunk("abc", "abc", "abc"))) - part2 <- push(None) - } yield assert(part1 ++ part2)(equalTo(Chunk("abcabcabc"))) - } + testSplitLines(Seq(Chunk("abc", "abc", "abc"))) }, testM("\\r\\n on the boundary") { - ZTransducer.splitLines.push.use { push => - for { - part1 <- push(Some(Chunk("abc\r", "\nabc"))) - part2 <- push(None) - } yield assert(part1 ++ part2)(equalTo(Chunk("abc", "abc"))) - } + testSplitLines(Seq(Chunk("abc\r", "\nabc"))) } ), suite("splitOn")( - testM("preserves data")(checkM(Gen.listOf(Gen.anyString.filter(!_.contains("|")).filter(_.nonEmpty))) { lines => - val data = lines.mkString("|") - val parser = ZTransducer.splitOn("|") - assertM(run(parser, List(Chunk.single(data))))(equalTo(lines)) + testM("preserves data")(checkM(Gen.chunkOf(Gen.anyString.filter(!_.contains("|")).filter(_.nonEmpty))) { + lines => + val data = lines.mkString("|") + val parser = ZTransducer.splitOn("|") + assertM(run(parser, List(Chunk.single(data))))(equalTo(lines)) }), testM("handles leftovers") { val parser = ZTransducer.splitOn("\n") - assertM(run(parser, List(Chunk("ab", "c\nb"), Chunk("c"))))(equalTo(List("abc", "bc"))) + assertM(run(parser, List(Chunk("ab", "c\nb"), Chunk("c"))))(equalTo(Chunk("abc", "bc"))) }, testM("aggregates") { assertM( Stream("abc", "delimiter", "bc", "delimiter", "bcd", "bcd") .aggregate(ZTransducer.splitOn("delimiter")) .runCollect - )(equalTo(List("abc", "bc", "bcdbcd"))) + )(equalTo(Chunk("abc", "bc", "bcdbcd"))) }, testM("single newline edgecase") { assertM( Stream("test") .aggregate(ZTransducer.splitOn("test")) .runCollect - )(equalTo(List(""))) + )(equalTo(Chunk(""))) }, testM("no delimiter in data") { assertM( Stream("abc", "abc", "abc") .aggregate(ZTransducer.splitOn("hello")) .runCollect - )(equalTo(List("abcabcabc"))) + )(equalTo(Chunk("abcabcabc"))) }, testM("delimiter on the boundary") { assertM( Stream("abc<", ">abc") .aggregate(ZTransducer.splitOn("<>")) .runCollect - )(equalTo(List("abc", "abc"))) + )(equalTo(Chunk("abc", "abc"))) } ), suite("utf8DecodeChunk")( @@ -498,7 +445,15 @@ object ZTransducerSpec extends ZIOBaseSpec { ) ) - val weirdStringGenForSplitLines = Gen - .listOf(Gen.string(Gen.printableChar).map(_.filterNot(c => c == '\n' || c == '\r'))) + val weirdStringGenForSplitLines: Gen[Random with Sized, Chunk[String]] = Gen + .chunkOf(Gen.string(Gen.printableChar).map(_.filterNot(c => c == '\n' || c == '\r'))) .map(l => if (l.nonEmpty && l.last == "") l ++ List("a") else l) + + def testSplitLines(input: Seq[Chunk[String]]): ZIO[Any, Nothing, TestResult] = { + val str = input.flatMap(_.mkString).mkString + val expected = Chunk.fromIterable(Source.fromString(str).getLines().toList) + ZStream.fromChunks(input: _*).transduce(ZTransducer.splitLines).runCollect.map { res => + assert(res)(equalTo(expected)) + } + } } diff --git a/streams/jvm/src/main/scala/zio/stream/platform.scala b/streams/jvm/src/main/scala/zio/stream/platform.scala index 79e1c983c470..27c20ee7f687 100644 --- a/streams/jvm/src/main/scala/zio/stream/platform.scala +++ b/streams/jvm/src/main/scala/zio/stream/platform.scala @@ -4,7 +4,8 @@ import java.io.{ IOException, InputStream, OutputStream } import java.net.InetSocketAddress import java.nio.channels.FileChannel import java.nio.channels.{ AsynchronousServerSocketChannel, AsynchronousSocketChannel, CompletionHandler } -import java.nio.file.Path +import java.nio.file.StandardOpenOption._ +import java.nio.file.{ OpenOption, Path } import java.nio.{ Buffer, ByteBuffer } import java.{ util => ju } @@ -31,6 +32,46 @@ trait ZSinkPlatformSpecificConstructors { self: ZSink.type => case e: IOException => e } } + + /** + * Uses the provided `Path` to create a [[ZSink]] that consumes byte chunks + * and writes them to the `File`. The sink will yield count of bytes written. + */ + final def fromFile( + path: => Path, + position: Long = 0L, + options: Set[OpenOption] = Set(WRITE, TRUNCATE_EXISTING, CREATE) + ): ZSink[Blocking, Throwable, Byte, Long] = + ZSink { + for { + state <- Ref.make(0L).toManaged_ + channel <- ZManaged.make( + blocking + .effectBlockingInterrupt( + FileChannel + .open( + path, + options.foldLeft(new ju.HashSet[OpenOption]()) { (acc, op) => + acc.add(op); acc + } // for avoiding usage of different Java collection converters for different scala versions + ) + .position(position) + ) + .orDie + )(chan => blocking.effectBlocking(chan.close()).orDie) + push = (is: Option[Chunk[Byte]]) => + is match { + case None => state.get.flatMap(Push.emit) + case Some(byteChunk) => + for { + justWritten <- blocking.effectBlockingInterrupt { + channel.write(ByteBuffer.wrap(byteChunk.toArray)) + }.mapError(Left(_)) + more <- state.update(_ + justWritten) *> Push.more + } yield more + } + } yield push + } } trait ZStreamPlatformSpecificConstructors { self: ZStream.type => diff --git a/streams/shared/src/main/scala/zio/stream/ZSink.scala b/streams/shared/src/main/scala/zio/stream/ZSink.scala index 38c05fb2157d..b01429b384eb 100644 --- a/streams/shared/src/main/scala/zio/stream/ZSink.scala +++ b/streams/shared/src/main/scala/zio/stream/ZSink.scala @@ -36,7 +36,9 @@ abstract class ZSink[-R, +E, -I, +Z] private ( /** * Operator alias for [[zipPar]]. */ - final def <&>[R1 <: R, E1 >: E, I1 <: I, Z1](that: ZSink[R1, E1, I1, Z1]): ZSink[R1, E1, I1, (Z, Z1)] = + final def <&>[R1 <: R, E1 >: E, I1 <: I, Z1]( + that: ZSink[R1, E1, I1, Z1] + ): ZSink[R1, E1, I1, (Z, Z1)] = self.zipPar(that) /** @@ -64,7 +66,8 @@ abstract class ZSink[-R, +E, -I, +Z] private ( /** * Operator alias for [[zipParLeft]]. */ - final def <&[R1 <: R, E1 >: E, I1 <: I](that: ZSink[R1, E1, I1, Any]): ZSink[R1, E1, I1, Z] = self.zipParLeft(that) + final def <&[R1 <: R, E1 >: E, I1 <: I](that: ZSink[R1, E1, I1, Any]): ZSink[R1, E1, I1, Z] = + self.zipParLeft(that) /** * Replaces this sink's result with the provided value. @@ -107,7 +110,9 @@ abstract class ZSink[-R, +E, -I, +Z] private ( /** * Effectfully transforms this sink's input chunks. */ - def contramapChunksM[R1 <: R, E1 >: E, I2](f: Chunk[I2] => ZIO[R1, E1, Chunk[I]]): ZSink[R1, E1, I2, Z] = + def contramapChunksM[R1 <: R, E1 >: E, I2]( + f: Chunk[I2] => ZIO[R1, E1, Chunk[I]] + ): ZSink[R1, E1, I2, Z] = ZSink[R1, E1, I2, Z]( self.push.map(push => input => @@ -190,8 +195,10 @@ abstract class ZSink[-R, +E, -I, +Z] private ( // to terminate. thisPush(None).catchAllCause { cause => val switchToNextPush = Cause.sequenceCauseEither(cause) match { - case Left(e) => openThatPush(failure(e).push).tap(thatPush.set) <* switched.set(true) - case Right(z) => openThatPush(success(z).push).tap(thatPush.set) <* switched.set(true) + case Left(e) => + openThatPush(failure(e).push).tap(thatPush.set) <* switched.set(true) + case Right(z) => + openThatPush(success(z).push).tap(thatPush.set) <* switched.set(true) } switchToNextPush.flatMap(_.apply(None)) @@ -200,8 +207,10 @@ abstract class ZSink[-R, +E, -I, +Z] private ( case is @ Some(_) => thisPush(is).catchAllCause { Cause.sequenceCauseEither(_) match { - case Left(e) => openThatPush(failure(e).push).flatMap(thatPush.set) *> switched.set(true) - case Right(z) => openThatPush(success(z).push).flatMap(thatPush.set) *> switched.set(true) + case Left(e) => + openThatPush(failure(e).push).flatMap(thatPush.set) *> switched.set(true) + case Right(z) => + openThatPush(success(z).push).flatMap(thatPush.set) *> switched.set(true) } } } @@ -279,9 +288,15 @@ abstract class ZSink[-R, +E, -I, +Z] private ( p1(in).raceWith(p2(in))( (res1, fib2) => res1 - .foldM(f => fib2.interrupt *> ZIO.halt(f.map(_.map(Left(_)))), _ => fib2.join.mapError(_.map(Right(_)))), + .foldM( + f => fib2.interrupt *> ZIO.halt(f.map(_.map(Left(_)))), + _ => fib2.join.mapError(_.map(Right(_))) + ), (res2, fib1) => - res2.foldM(f => fib1.interrupt *> ZIO.halt(f.map(_.map(Right(_)))), _ => fib1.join.mapError(_.map(Left(_)))) + res2.foldM( + f => fib1.interrupt *> ZIO.halt(f.map(_.map(Right(_)))), + _ => fib1.join.mapError(_.map(Left(_))) + ) ) } } yield push) @@ -341,7 +356,9 @@ abstract class ZSink[-R, +E, -I, +Z] private ( */ final def zipWith[R1 <: R, E1 >: E, I1 <: I, Z1, Z2]( that: ZSink[R1, E1, I1, Z1] - )(f: (Z, Z1) => Z2): ZSink[R1, E1, I1, Z2] = + )( + f: (Z, Z1) => Z2 + ): ZSink[R1, E1, I1, Z2] = flatMap(z => that.map(f(z, _))) /** @@ -350,7 +367,9 @@ abstract class ZSink[-R, +E, -I, +Z] private ( */ final def zipWithPar[R1 <: R, E1 >: E, I1 <: I, Z1, Z2]( that: ZSink[R1, E1, I1, Z1] - )(f: (Z, Z1) => Z2): ZSink[R1, E1, I1, Z2] = { + )( + f: (Z, Z1) => Z2 + ): ZSink[R1, E1, I1, Z2] = { sealed trait State[+Z, +Z1] case object BothRunning extends State[Nothing, Nothing] @@ -453,7 +472,7 @@ object ZSink extends ZSinkPlatformSpecificConstructors { */ def restartable[R, E, I, Z]( sink: ZManaged[R, Nothing, Push[R, E, I, Z]] - ): ZManaged[R, Nothing, (Push[R, E, I, Z], ZIO[R, Nothing, Unit])] = + ): ZManaged[R, Nothing, (Push[R, E, I, Z], URIO[R, Unit])] = for { switchSink <- ZManaged.switchable[R, Nothing, Push[R, E, I, Z]] initialSink <- switchSink(sink).toManaged_ @@ -467,10 +486,15 @@ object ZSink extends ZSinkPlatformSpecificConstructors { new ZSink(push) {} /** - * A sink that collects all of its inputs into a list. + * A sink that collects all of its inputs into a chunk. */ - def collectAll[A]: ZSink[Any, Nothing, A, List[A]] = - foldLeftChunks(Chunk[A]())(_ ++ (_: Chunk[A])).map(_.toList) + def collectAll[A]: ZSink[Any, Nothing, A, Chunk[A]] = ZSink { + for { + builder <- UIO(ChunkBuilder.make[A]()).toManaged_ + foldingSink = foldLeftChunks(builder)((b, chunk: Chunk[A]) => b ++= chunk).map(_.result()) + push <- foldingSink.push + } yield push + } /** * A sink that collects all of its inputs into a map. The keys are extracted from inputs @@ -537,7 +561,13 @@ object ZSink extends ZSinkPlatformSpecificConstructors { /** * A sink that folds its input chunks with the provided function, termination predicate and initial state. */ - def foldChunks[I, S](z: S)(contFn: S => Boolean)(f: (S, Chunk[I]) => S): ZSink[Any, Nothing, I, S] = + def foldChunks[I, S]( + z: S + )( + contFn: S => Boolean + )( + f: (S, Chunk[I]) => S + ): ZSink[Any, Nothing, I, S] = foldChunksM(z)(contFn)((s, is) => UIO.succeedNow(f(s, is))) /** @@ -546,7 +576,13 @@ object ZSink extends ZSinkPlatformSpecificConstructors { * This sink may terminate in the middle of a chunk and discard the rest of it. See the discussion on the * ZSink class scaladoc on sinks vs. transducers. */ - def foldChunksM[R, E, I, S](z: S)(contFn: S => Boolean)(f: (S, Chunk[I]) => ZIO[R, E, S]): ZSink[R, E, I, S] = + def foldChunksM[R, E, I, S]( + z: S + )( + contFn: S => Boolean + )( + f: (S, Chunk[I]) => ZIO[R, E, S] + ): ZSink[R, E, I, S] = if (contFn(z)) ZSink { for { diff --git a/streams/shared/src/main/scala/zio/stream/ZStream.scala b/streams/shared/src/main/scala/zio/stream/ZStream.scala index a2058dc8fc11..c5ecd80c0209 100644 --- a/streams/shared/src/main/scala/zio/stream/ZStream.scala +++ b/streams/shared/src/main/scala/zio/stream/ZStream.scala @@ -186,7 +186,7 @@ abstract class ZStream[-R, +E, +O](val process: ZManaged[R, Nothing, ZIO[R, Opti * @tparam E1 error type * @tparam O1 type of the values consumed by the given transducer * @tparam P type of the value produced by the given transducer and consumed by the given schedule - * @return `ZStream[R1, E1, B]` + * @return `ZStream[R1, E1, P]` */ final def aggregateAsyncWithin[R1 <: R, E1 >: E, P]( transducer: ZTransducer[R1, E1, O, P], @@ -226,7 +226,8 @@ abstract class ZStream[-R, +E, +O](val process: ZManaged[R, Nothing, ZIO[R, Opti push <- transducer.push handoff <- ZStream.Handoff.make[Take[E1, O]].toManaged_ raceNextTime <- ZRef.makeManaged(false) - waitingFiber <- ZRef.makeManaged[Option[Fiber[Nothing, Take[E1, O]]]](None) + waitingFiber <- ZRef + .makeManaged[Option[Fiber[Nothing, Take[E1, O]]]](None) scheduleState <- schedule.initial .flatMap(i => ZRef.make[(Chunk[P], schedule.State)](Chunk.empty -> i)) .toManaged_ @@ -274,6 +275,7 @@ abstract class ZStream[-R, +E, +O](val process: ZManaged[R, Nothing, ZIO[R, Opti scheduleResult = Take.single(Left(schedule.extract(state._1, state._2))) take <- Take.fromPull(push(None).asSomeError).tap(updateLastChunk) _ <- raceNextTime.set(false) + _ <- producerWaiting.disown // To avoid interruption when this fiber is joined _ <- waitingFiber.set(Some(producerWaiting)) } yield Chunk(scheduleResult, take.map(Right(_))) case Some(nextState) => @@ -281,6 +283,7 @@ abstract class ZStream[-R, +E, +O](val process: ZManaged[R, Nothing, ZIO[R, Opti _ <- scheduleState.update(_.copy(_2 = nextState)) ps <- Take.fromPull(push(None).asSomeError).tap(updateLastChunk) _ <- raceNextTime.set(false) + _ <- producerWaiting.disown // To avoid interruption when this fiber is joined _ <- waitingFiber.set(Some(producerWaiting)) } yield Chunk.single(ps.map(Right(_))) }, @@ -288,7 +291,9 @@ abstract class ZStream[-R, +E, +O](val process: ZManaged[R, Nothing, ZIO[R, Opti scheduleWaiting.interrupt *> handleTake(Take(producerDone.flatMap(_.exit))) ) - raceNextTime.get.flatMap(go) + raceNextTime.get + .flatMap(go) + .onInterrupt(waitingFiber.get.flatMap(_.map(_.interrupt).getOrElse(ZIO.unit))) } @@ -1688,15 +1693,25 @@ abstract class ZStream[-R, +E, +O](val process: ZManaged[R, Nothing, ZIO[R, Opti final def intersperse[O1 >: O](middle: O1): ZStream[R, E, O1] = ZStream { for { - state <- ZRef.makeManaged(false) + state <- ZRef.makeManaged(true) chunks <- self.process pull = chunks.flatMap { os => - state.modify { flag => - os.foldRight(List.empty[O1] -> flag) { - case (o, (Nil, curr)) => List(o) -> !curr - case (o, (out, curr)) => (o :: middle :: out) -> !curr + state.modify { first => + val builder = ChunkBuilder.make[O1]() + var flagResult = first + + os.foreach { o => + if (flagResult) { + flagResult = false + builder += o + } else { + builder += middle + builder += o + } } - }.map(e => Chunk.fromIterable(e)) + + (builder.result(), flagResult) + } } } yield pull } @@ -1909,7 +1924,7 @@ abstract class ZStream[-R, +E, +O](val process: ZManaged[R, Nothing, ZIO[R, Opti } yield () }.foldCauseM( c => out.offer(Pull.halt(c)).unit.toManaged_, - _ => (out.offer(Pull.end) <* ZIO.awaitAllChildren).unit.toManaged_ + _ => (permits.withPermits(n.toLong)(ZIO.unit).interruptible *> out.offer(Pull.end)).toManaged_ ) .fork } yield out.take.flatten.map(Chunk.single(_)) @@ -2295,7 +2310,7 @@ abstract class ZStream[-R, +E, +O](val process: ZManaged[R, Nothing, ZIO[R, Opti /** * Runs the stream and collects all of its elements to a list. */ - def runCollect: ZIO[R, E, List[O]] = run(ZSink.collectAll[O]) + def runCollect: ZIO[R, E, Chunk[O]] = run(ZSink.collectAll[O]) /** * Runs the stream and emits the number of elements processed @@ -2781,8 +2796,7 @@ abstract class ZStream[-R, +E, +O](val process: ZManaged[R, Nothing, ZIO[R, Opti for { runtime <- ZIO.runtime[R].toManaged_ pull <- process.asInstanceOf[ZManaged[R, Nothing, ZIO[R, Option[Throwable], Chunk[Byte]]]] - is <- Task(ZInputStream.fromPull(runtime, pull)).asInstanceOf[ZIO[R, E, java.io.InputStream]].toManaged_ - } yield is + } yield ZInputStream.fromPull(runtime, pull) /** * Converts this stream into a `scala.collection.Iterator` wrapped in a [[ZManaged]]. @@ -3025,7 +3039,7 @@ abstract class ZStream[-R, +E, +O](val process: ZManaged[R, Nothing, ZIO[R, Opti that: ZStream[R1, E1, O2] )(f: (O, O2) => O3): ZStream[R1, E1, O3] = { def pullNonEmpty[R, E, O](pull: ZIO[R, Option[E], Chunk[O]]): ZIO[R, Option[E], Chunk[O]] = - pull.flatMap(chunk => if (chunk.isEmpty) pull else UIO.succeedNow(chunk)) + pull.flatMap(chunk => if (chunk.isEmpty) pullNonEmpty(pull) else UIO.succeedNow(chunk)) ZStream { for { @@ -3152,7 +3166,7 @@ object ZStream extends ZStreamPlatformSpecificConstructors { * Creates a stream from a single value that will get cleaned up after the * stream is consumed */ - def bracket[R, E, A](acquire: ZIO[R, E, A])(release: A => ZIO[R, Nothing, Any]): ZStream[R, E, A] = + def bracket[R, E, A](acquire: ZIO[R, E, A])(release: A => URIO[R, Any]): ZStream[R, E, A] = managed(ZManaged.make(acquire)(release)) /** @@ -3161,7 +3175,7 @@ object ZStream extends ZStreamPlatformSpecificConstructors { */ def bracketExit[R, E, A]( acquire: ZIO[R, E, A] - )(release: (A, Exit[Any, Any]) => ZIO[R, Nothing, Any]): ZStream[R, E, A] = + )(release: (A, Exit[Any, Any]) => URIO[R, Any]): ZStream[R, E, A] = managed(ZManaged.makeExit(acquire)(release)) /** @@ -3286,7 +3300,7 @@ object ZStream extends ZStreamPlatformSpecificConstructors { /** * Creates a one-element stream that never fails and executes the finalizer when it ends. */ - def finalizer[R](finalizer: ZIO[R, Nothing, Any]): ZStream[R, Nothing, Any] = + def finalizer[R](finalizer: URIO[R, Any]): ZStream[R, Nothing, Any] = bracket[R, Nothing, Unit](UIO.unit)(_ => finalizer) /** @@ -3585,6 +3599,12 @@ object ZStream extends ZStreamPlatformSpecificConstructors { def range(min: Int, max: Int): ZStream[Any, Nothing, Int] = iterate(min)(_ + 1).takeWhile(_ < max) + /** + * Repeats the provided value infinitely. + */ + def repeat[A](a: => A): ZStream[Any, Nothing, A] = + repeatEffect(UIO.succeed(a)) + /** * Creates a stream from an effect producing a value of type `A` which repeats forever. */ @@ -3607,7 +3627,19 @@ object ZStream extends ZStreamPlatformSpecificConstructors { * Creates a stream from an effect producing chunks of `A` values until it fails with None. */ def repeatEffectChunkOption[R, E, A](fa: ZIO[R, Option[E], Chunk[A]]): ZStream[R, E, A] = - ZStream(ZManaged.succeedNow(fa)) + ZStream { + for { + done <- Ref.make(false).toManaged_ + pull = done.get.flatMap { + if (_) Pull.end + else + fa.tapError { + case None => done.set(true) + case Some(_) => ZIO.unit + } + } + } yield pull + } /** * Creates a stream from an effect producing a value of type `A` which repeats using the specified schedule @@ -3619,6 +3651,12 @@ object ZStream extends ZStreamPlatformSpecificConstructors { } } + /** + * Repeats the value using the provided schedule. + */ + def repeatWith[R, A](a: => A, schedule: Schedule[R, A, _]): ZStream[R, Nothing, A] = + repeatEffectWith(UIO.succeed(a), schedule) + /** * Accesses the specified service in the environment of the effect. */ @@ -3650,6 +3688,12 @@ object ZStream extends ZStreamPlatformSpecificConstructors { def succeed[A](a: => A): ZStream[Any, Nothing, A] = fromChunk(Chunk.single(a)) + /** + * A stream that emits Unit values spaced by the specified duration. + */ + def tick(interval: Duration): ZStream[Clock, Nothing, Unit] = + repeatWith((), Schedule.spaced(interval)) + /** * A stream that contains a single `Unit` value. */ diff --git a/streams/shared/src/main/scala/zio/stream/ZTransducer.scala b/streams/shared/src/main/scala/zio/stream/ZTransducer.scala index 1127604e1132..f3c1e1ceb541 100644 --- a/streams/shared/src/main/scala/zio/stream/ZTransducer.scala +++ b/streams/shared/src/main/scala/zio/stream/ZTransducer.scala @@ -31,75 +31,6 @@ abstract class ZTransducer[-R, +E, -I, +O](val push: ZManaged[R, Nothing, Option } } - /** - * Symbolic alias for [[ZTransducer#zip]]. - */ - final def <&>[R1 <: R, E1 >: E, I1 <: I, O2](that: ZTransducer[R1, E1, I1, O2]): ZTransducer[R1, E1, I1, (O, O2)] = - self zip that - - /** - * Symbolic alias for [[ZTransducer#zipLeft]]. - */ - final def <&[R1 <: R, E1 >: E, I1 <: I, O2](that: ZTransducer[R1, E1, I1, O2]): ZTransducer[R1, E1, I1, O] = - self zipLeft that - - /** - * Symbolic alias for [[ZTransducer#zipRight]]. - */ - final def &>[R1 <: R, E1 >: E, I1 <: I, O2](that: ZTransducer[R1, E1, I1, O2]): ZTransducer[R1, E1, I1, O2] = - self zipRight that - - /** - * Zips this transducer with another point-wise, but keeps only the outputs of this transducer. - */ - def zipLeft[R1 <: R, E1 >: E, I1 <: I, O2](that: ZTransducer[R1, E1, I1, O2]): ZTransducer[R1, E1, I1, O] = - zipWith(that)((o, _) => o) - - /** - * Zips this transducer with another point-wise, but keeps only the outputs of the other transducer. - */ - def zipRight[R1 <: R, E1 >: E, I1 <: I, O2](that: ZTransducer[R1, E1, I1, O2]): ZTransducer[R1, E1, I1, O2] = - zipWith(that)((_, o2) => o2) - - /** - * Zips this transducer with another point-wise and emits tuples of elements from both transducers. - */ - def zip[R1 <: R, E1 >: E, I1 <: I, O2](that: ZTransducer[R1, E1, I1, O2]): ZTransducer[R1, E1, I1, (O, O2)] = - zipWith(that)((_, _)) - - /** - * Zips this transducer with another point-wise and applies the function to the paired elements. - */ - def zipWith[R1 <: R, E1 >: E, I1 <: I, O2, O3]( - that: ZTransducer[R1, E1, I1, O2] - )(f: (O, O2) => O3): ZTransducer[R1, E1, I1, O3] = { - type State = Either[Chunk[O], Chunk[O2]] - ZTransducer { - for { - ref <- ZRef.make[State](Left(Chunk.empty)).toManaged_ - p1 <- self.push - p2 <- that.push - push = (in: Option[Chunk[I1]]) => { - ref.get.flatMap { excess => - for { - res <- p1(in).zipWithPar(p2(in)) { - case (leftUpd, rightUpd) => - val (left, right) = excess.fold(l => (l ++ leftUpd, rightUpd), r => (leftUpd, r ++ rightUpd)) - stream.internal.Utils.zipChunks(left, right, f) - } - (emit, newExcess) = res - _ <- ref.set(in.fold(Left(Chunk.empty): Either[Chunk[O], Chunk[O2]])(_ => newExcess)) - } yield { - emit - } - } - } - } yield { - push - } - } - } - /** * Compose this transducer with a sink, resulting in a sink that processes elements by piping * them through this transducer and piping the results into the sink. @@ -303,7 +234,7 @@ object ZTransducer { if (contFn(o)) (os0, o, true) else - (os0 + o, z, false) + (os0 :+ o, z, false) } ZRef.makeManaged[Option[O]](Some(z)).map { state => @@ -349,7 +280,7 @@ object ZTransducer { if (contFn(o)) (os0, o, true) else - (os0 + o, z, false) + (os0 :+ o, z, false) } } @@ -457,14 +388,14 @@ object ZTransducer { // If `i` cannot be decomposed, we need to cross the `max` threshold. To // minimize "injury", we only allow this when we haven't added anything else // to the aggregate (dirty = false). - (os0 + f(state.result, if (is.nonEmpty) is(0) else i), initial, false) + (os0 :+ f(state.result, if (is.nonEmpty) is(0) else i), initial, false) else if (is.length <= 1 && dirty) { // If the state is dirty and `i` cannot be decomposed, we close the current // aggregate and a create new one from `is`. We're not adding `f(initial, i)` to // the results immediately because it could be that `i` by itself does not // cross the threshold, so we can attempt to aggregate it with subsequent elements. val elem = if (is.nonEmpty) is(0) else i - (os0 + state.result, FoldWeightedState(f(initial.result, elem), costFn(initial.result, elem)), true) + (os0 :+ state.result, FoldWeightedState(f(initial.result, elem), costFn(initial.result, elem)), true) } else // `i` got decomposed, so we will recurse and see whether the decomposition // can be aggregated without crossing `max`. @@ -539,12 +470,12 @@ object ZTransducer { decompose(i).flatMap(is => // See comments on `foldWeightedDecompose` for details on every case here. if (is.length <= 1 && !dirty) - f(state.result, if (is.nonEmpty) is(0) else i).map(o => ((os + o), initial, false)) + f(state.result, if (is.nonEmpty) is(0) else i).map(o => ((os :+ o), initial, false)) else if (is.length <= 1 && dirty) { val elem = if (is.nonEmpty) is(0) else i f(initial.result, elem).zipWith(costFn(initial.result, elem)) { (s, cost) => - (os + state.result, FoldWeightedState(s, cost), true) + (os :+ state.result, FoldWeightedState(s, cost), true) } } else go(is, os, state, dirty) ) diff --git a/streams/shared/src/main/scala/zio/stream/internal/ZInputStream.scala b/streams/shared/src/main/scala/zio/stream/internal/ZInputStream.scala index 65d4579c8abc..c017af966233 100644 --- a/streams/shared/src/main/scala/zio/stream/internal/ZInputStream.scala +++ b/streams/shared/src/main/scala/zio/stream/internal/ZInputStream.scala @@ -103,6 +103,6 @@ private[zio] object ZInputStream { case Right(c) => throw FiberFailure(c) } } - new ZInputStream(unfoldPull) + new ZInputStream(Iterator.empty ++ unfoldPull) } } diff --git a/test-tests/shared/src/main/scala/zio/test/mock/modules.scala b/test-tests/shared/src/main/scala/zio/test/mock/modules.scala index cd5da8248a3c..24c91ff96c4a 100644 --- a/test-tests/shared/src/main/scala/zio/test/mock/modules.scala +++ b/test-tests/shared/src/main/scala/zio/test/mock/modules.scala @@ -1,6 +1,6 @@ package zio.test.mock -import zio.{ Has, IO, Tag, ZIO } +import zio.{ Has, IO, Tag, UIO } /** * https://github.com/scalamacros/paradise/issues/75 @@ -16,7 +16,7 @@ object modules { type SinglePureValModule = Has[SinglePureValModule.Service] object SinglePureValModule { trait Service { - val foo: ZIO[Any, Nothing, Unit] + val foo: UIO[Unit] } } diff --git a/test-tests/shared/src/test/scala/zio/test/GenUtils.scala b/test-tests/shared/src/test/scala/zio/test/GenUtils.scala index 9d7c68539c5e..903ff24e6288 100644 --- a/test-tests/shared/src/test/scala/zio/test/GenUtils.scala +++ b/test-tests/shared/src/test/scala/zio/test/GenUtils.scala @@ -5,27 +5,27 @@ import zio.random.Random import zio.stream.ZStream import zio.test.Assertion.{ equalTo, forall } import zio.test.environment.TestRandom -import zio.{ Exit, UIO, ZIO } +import zio.{ Exit, UIO, URIO, ZIO } object GenUtils { - def alwaysShrinksTo[R, A](gen: Gen[R, A])(a: A): ZIO[R, Nothing, TestResult] = { + def alwaysShrinksTo[R, A](gen: Gen[R, A])(a: A): URIO[R, TestResult] = { val shrinks = if (TestPlatform.isJS) 1 else 100 ZIO.collectAll(List.fill(shrinks)(shrinksTo(gen))).map(assert(_)(forall(equalTo(a)))) } def checkFinite[A, B]( gen: Gen[Random, A] - )(assertion: Assertion[B], f: List[A] => B = (a: List[A]) => a): ZIO[Random, Nothing, TestResult] = - assertM(gen.sample.map(_.value).runCollect.map(f))(assertion) + )(assertion: Assertion[B], f: List[A] => B = (a: List[A]) => a): URIO[Random, TestResult] = + assertM(gen.sample.map(_.value).runCollect.map(xs => f(xs.toList)))(assertion) def checkSample[A, B]( gen: Gen[Random with Sized, A], size: Int = 100 - )(assertion: Assertion[B], f: List[A] => B = (a: List[A]) => a): ZIO[Random, Nothing, TestResult] = + )(assertion: Assertion[B], f: List[A] => B = (a: List[A]) => a): URIO[Random, TestResult] = assertM(provideSize(sample100(gen).map(f))(size))(assertion) - def checkShrink[A](gen: Gen[Random with Sized, A])(a: A): ZIO[Random, Nothing, TestResult] = + def checkShrink[A](gen: Gen[Random with Sized, A])(a: A): URIO[Random, TestResult] = provideSize(alwaysShrinksTo(gen)(a: A))(100) val deterministic: Gen[Random with Sized, Gen[Any, Int]] = @@ -66,25 +66,25 @@ object GenUtils { case e @ Failure(_) => Left(e) } - def provideSize[A](zio: ZIO[Random with Sized, Nothing, A])(n: Int): ZIO[Random, Nothing, A] = + def provideSize[A](zio: ZIO[Random with Sized, Nothing, A])(n: Int): URIO[Random, A] = zio.provideLayer[Nothing, Random, Random with Sized](Random.any ++ Sized.live(n)) val random: Gen[Any, Gen[Random, Int]] = Gen.const(Gen.int(-10, 10)) def shrinks[R, A](gen: Gen[R, A]): ZIO[R, Nothing, List[A]] = - gen.sample.forever.take(1).flatMap(_.shrinkSearch(_ => true)).take(1000).runCollect + gen.sample.forever.take(1).flatMap(_.shrinkSearch(_ => true)).take(1000).runCollect.map(_.toList) - def shrinksTo[R, A](gen: Gen[R, A]): ZIO[R, Nothing, A] = + def shrinksTo[R, A](gen: Gen[R, A]): URIO[R, A] = shrinks(gen).map(_.reverse.head) val smallInt = Gen.int(-10, 10) def sample[R, A](gen: Gen[R, A]): ZIO[R, Nothing, List[A]] = - gen.sample.map(_.value).runCollect + gen.sample.map(_.value).runCollect.map(_.toList) def sample100[R, A](gen: Gen[R, A]): ZIO[R, Nothing, List[A]] = - gen.sample.map(_.value).forever.take(100).runCollect + gen.sample.map(_.value).forever.take(100).runCollect.map(_.toList) def sampleEffect[E, A]( gen: Gen[Random with Sized, ZIO[Random with Sized, E, A]], @@ -92,14 +92,14 @@ object GenUtils { ): ZIO[Random, Nothing, List[Exit[E, A]]] = provideSize(sample100(gen).flatMap(effects => ZIO.foreach(effects)(_.run)))(size) - def shrink[R, A](gen: Gen[R, A]): ZIO[R, Nothing, A] = + def shrink[R, A](gen: Gen[R, A]): URIO[R, A] = gen.sample.take(1).flatMap(_.shrinkSearch(_ => true)).take(1000).runLast.map(_.get) val shrinkable: Gen[Random, Int] = Gen.fromRandomSample(_.nextIntBounded(90).map(_ + 10).map(Sample.shrinkIntegral(0))) def shrinkWith[R, A](gen: Gen[R, A])(f: A => Boolean): ZIO[R, Nothing, List[A]] = - gen.sample.take(1).flatMap(_.shrinkSearch(!f(_))).take(1000).filter(!f(_)).runCollect + gen.sample.take(1).flatMap(_.shrinkSearch(!f(_))).take(1000).filter(!f(_)).runCollect.map(_.toList) val three = Gen(ZStream(Sample.unfold[Any, Int, Int](3) { n => if (n == 0) (n, ZStream.empty) diff --git a/test-tests/shared/src/test/scala/zio/test/ManagedSpec.scala b/test-tests/shared/src/test/scala/zio/test/ManagedSpec.scala index f12da791e01f..5a50a6043327 100644 --- a/test-tests/shared/src/test/scala/zio/test/ManagedSpec.scala +++ b/test-tests/shared/src/test/scala/zio/test/ManagedSpec.scala @@ -23,7 +23,7 @@ object ManagedSpec extends ZIOBaseSpec { } } - val incrementAndGet: ZIO[Counter, Nothing, Int] = + val incrementAndGet: URIO[Counter, Int] = ZIO.accessM[Counter](_.get[Counter.Service].incrementAndGet) } diff --git a/test-tests/shared/src/test/scala/zio/test/TestUtils.scala b/test-tests/shared/src/test/scala/zio/test/TestUtils.scala index 93264c4424cc..c18a68203add 100644 --- a/test-tests/shared/src/test/scala/zio/test/TestUtils.scala +++ b/test-tests/shared/src/test/scala/zio/test/TestUtils.scala @@ -10,7 +10,7 @@ object TestUtils { def forAllTests[E]( execSpec: UIO[ExecutedSpec[E]] - )(f: Either[TestFailure[E], TestSuccess] => Boolean): ZIO[Any, Nothing, Boolean] = + )(f: Either[TestFailure[E], TestSuccess] => Boolean): UIO[Boolean] = execSpec.flatMap { results => results.forall { case Spec.TestCase(_, test, _) => test.map(r => f(r)) @@ -18,7 +18,7 @@ object TestUtils { }.useNow } - def isIgnored[E](spec: ZSpec[environment.TestEnvironment, E]): ZIO[Any, Nothing, Boolean] = { + def isIgnored[E](spec: ZSpec[environment.TestEnvironment, E]): UIO[Boolean] = { val execSpec = execute(spec) forAllTests(execSpec) { case Right(TestSuccess.Ignored) => true @@ -26,7 +26,7 @@ object TestUtils { } } - def succeeded[E](spec: ZSpec[environment.TestEnvironment, E]): ZIO[Any, Nothing, Boolean] = { + def succeeded[E](spec: ZSpec[environment.TestEnvironment, E]): UIO[Boolean] = { val execSpec = execute(spec) forAllTests(execSpec) { case Right(TestSuccess.Succeeded(_)) => true diff --git a/test-tests/shared/src/test/scala/zio/test/environment/RandomSpec.scala b/test-tests/shared/src/test/scala/zio/test/environment/RandomSpec.scala index a6de28e07594..edbba075b3aa 100644 --- a/test-tests/shared/src/test/scala/zio/test/environment/RandomSpec.scala +++ b/test-tests/shared/src/test/scala/zio/test/environment/RandomSpec.scala @@ -81,7 +81,7 @@ object RandomSpec extends ZIOBaseSpec { def checkClear[A, B <: Random](generate: SRandom => A)(feed: (ZRandom, List[A]) => UIO[Unit])( clear: ZRandom => UIO[Unit] - )(extract: ZRandom => UIO[A]): ZIO[Random, Nothing, TestResult] = + )(extract: ZRandom => UIO[A]): URIO[Random, TestResult] = checkM(Gen.anyLong) { seed => for { sRandom <- ZIO.effectTotal(new SRandom(seed)) @@ -97,7 +97,7 @@ object RandomSpec extends ZIOBaseSpec { def checkFeed[A, B >: Random](generate: SRandom => A)( feed: (ZRandom, List[A]) => UIO[Unit] - )(extract: ZRandom => UIO[A]): ZIO[Random, Nothing, TestResult] = + )(extract: ZRandom => UIO[A]): URIO[Random, TestResult] = checkM(Gen.anyLong) { seed => for { sRandom <- ZIO.effectTotal(new SRandom(seed)) @@ -122,7 +122,7 @@ object RandomSpec extends ZIOBaseSpec { def forAllEqual[A]( f: ZRandom => UIO[A] - )(g: SRandom => A): ZIO[Random, Nothing, TestResult] = + )(g: SRandom => A): URIO[Random, TestResult] = checkM(Gen.anyLong) { seed => for { sRandom <- ZIO.effectTotal(new SRandom(seed)) @@ -133,7 +133,7 @@ object RandomSpec extends ZIOBaseSpec { } yield assert(actual)(equalTo(expected)) } - def forAllEqualBytes: ZIO[Random, Nothing, TestResult] = + def forAllEqualBytes: URIO[Random, TestResult] = checkM(Gen.anyLong) { seed => for { sRandom <- ZIO.effectTotal(new SRandom(seed)) @@ -147,7 +147,7 @@ object RandomSpec extends ZIOBaseSpec { } yield assert(actual)(equalTo(expected)) } - def forAllEqualGaussian: ZIO[Random, Nothing, TestResult] = + def forAllEqualGaussian: URIO[Random, TestResult] = checkM(Gen.anyLong) { seed => for { sRandom <- ZIO.effectTotal(new SRandom(seed)) @@ -160,7 +160,7 @@ object RandomSpec extends ZIOBaseSpec { def forAllEqualN[A]( f: (ZRandom, Int) => UIO[A] - )(g: (SRandom, Int) => A): ZIO[Random, Nothing, TestResult] = + )(g: (SRandom, Int) => A): URIO[Random, TestResult] = checkM(Gen.anyLong, Gen.int(1, 100)) { (seed, size) => for { sRandom <- ZIO.effectTotal(new SRandom(seed)) @@ -186,7 +186,7 @@ object RandomSpec extends ZIOBaseSpec { def forAllBounded[A: Numeric](gen: Gen[Random, A])( next: (Random.Service, A) => UIO[A] - ): ZIO[Random, Nothing, TestResult] = { + ): URIO[Random, TestResult] = { val num = implicitly[Numeric[A]] import num._ checkM(gen.map(num.abs(_))) { upper => @@ -199,7 +199,7 @@ object RandomSpec extends ZIOBaseSpec { def forAllBetween[A: Numeric](gen: Gen[Random, A])( between: (Random.Service, A, A) => UIO[A] - ): ZIO[Random, Nothing, TestResult] = { + ): URIO[Random, TestResult] = { val num = implicitly[Numeric[A]] import num._ val genMinMax = for { diff --git a/test-tests/shared/src/test/scala/zio/test/mock/BasicStreamMockSpec.scala b/test-tests/shared/src/test/scala/zio/test/mock/BasicStreamMockSpec.scala index 6e8a53bcd784..6d0fabaa0408 100644 --- a/test-tests/shared/src/test/scala/zio/test/mock/BasicStreamMockSpec.scala +++ b/test-tests/shared/src/test/scala/zio/test/mock/BasicStreamMockSpec.scala @@ -1,5 +1,6 @@ package zio.test.mock +import zio.Chunk import zio.stream.{ ZSink, ZStream } import zio.test.mock.module.{ StreamModule, StreamModuleMock } import zio.test.{ suite, Assertion, TestAspect, ZIOBaseSpec } @@ -17,7 +18,7 @@ object BasicStreamMockSpec extends ZIOBaseSpec with MockSpecUtils[StreamModule] suite("capabilities")( suite("sink")( testValue("success")( - StreamModuleMock.Sink(equalTo(1), value(ZSink.collectAll)), + StreamModuleMock.Sink(equalTo(1), value(ZSink.collectAll.map(_.toList))), StreamModule.sink(1).flatMap(A.run(_)), equalTo(List(1, 2, 3)) ), @@ -31,7 +32,7 @@ object BasicStreamMockSpec extends ZIOBaseSpec with MockSpecUtils[StreamModule] testValue("success")( StreamModuleMock.Stream(equalTo(1), value(A)), StreamModule.stream(1).flatMap(_.runCollect), - equalTo(List(1, 2, 3)) + equalTo(Chunk(1, 2, 3)) ) ) ) diff --git a/test-tests/shared/src/test/scala/zio/test/mock/MockSpecUtils.scala b/test-tests/shared/src/test/scala/zio/test/mock/MockSpecUtils.scala index 94aa27e0371d..0e6761753b26 100644 --- a/test-tests/shared/src/test/scala/zio/test/mock/MockSpecUtils.scala +++ b/test-tests/shared/src/test/scala/zio/test/mock/MockSpecUtils.scala @@ -19,7 +19,7 @@ import zio.duration._ import zio.test.environment.Live import zio.test.mock.module.T22 import zio.test.{ assertM, testM, Assertion, ZSpec } -import zio.{ ULayer, ZIO } +import zio.{ IO, ULayer, ZIO } trait MockSpecUtils[R] { @@ -64,7 +64,7 @@ trait MockSpecUtils[R] { app: ZIO[R, E, A], check: Assertion[Throwable] ): ZSpec[Any, Any] = testM(name) { - val result: ZIO[Any, Any, Throwable] = + val result: IO[Any, Throwable] = mock.build .use(app.provide _) .orElse(ZIO.unit) diff --git a/test-tests/shared/src/test/scala/zio/test/mock/module/PureModule.scala b/test-tests/shared/src/test/scala/zio/test/mock/module/PureModule.scala index 94a11833acc8..bc9bf84597dd 100644 --- a/test-tests/shared/src/test/scala/zio/test/mock/module/PureModule.scala +++ b/test-tests/shared/src/test/scala/zio/test/mock/module/PureModule.scala @@ -18,7 +18,7 @@ package zio.test.mock.module import scala.reflect.ClassTag -import zio.{ IO, Tag, ZIO } +import zio.{ IO, Tag, URIO, ZIO } /** * Example module used for testing ZIO Mock framework. @@ -94,7 +94,7 @@ object PureModule { ZIO.accessM[PureModule](_.get.manyParamLists(a)(b)(c)) def command: ZIO[PureModule, Unit, Unit] = ZIO.accessM[PureModule](_.get.command) def parameterizedCommand(a: Int): ZIO[PureModule, Unit, Unit] = ZIO.accessM[PureModule](_.get.parameterizedCommand(a)) - def looped(a: Int): ZIO[PureModule, Nothing, Nothing] = ZIO.accessM[PureModule](_.get.looped(a)) + def looped(a: Int): URIO[PureModule, Nothing] = ZIO.accessM[PureModule](_.get.looped(a)) def overloaded(n: Int): ZIO[PureModule, String, String] = ZIO.accessM[PureModule](_.get.overloaded(n)) def overloaded(n: Long): ZIO[PureModule, String, String] = ZIO.accessM[PureModule](_.get.overloaded(n)) def polyInput[I: NotAnyKind: Tag](v: I): ZIO[PureModule, String, String] = diff --git a/test/shared/src/main/scala/zio/test/AssertionM.scala b/test/shared/src/main/scala/zio/test/AssertionM.scala index d33a727e764b..724ed2da2456 100644 --- a/test/shared/src/main/scala/zio/test/AssertionM.scala +++ b/test/shared/src/main/scala/zio/test/AssertionM.scala @@ -19,7 +19,7 @@ package zio.test import scala.reflect.ClassTag import scala.util.Try -import zio.ZIO +import zio.{ UIO, ZIO } /** * An `AssertionM[A]` is capable of producing assertion results on an `A`. As a @@ -173,7 +173,7 @@ object AssertionM { */ def assertionM[R, E, A]( name: String - )(params: RenderParam*)(run: (=> A) => ZIO[Any, Nothing, Boolean]): AssertionM[A] = { + )(params: RenderParam*)(run: (=> A) => UIO[Boolean]): AssertionM[A] = { lazy val assertion: AssertionM[A] = assertionDirect(name)(params: _*) { actual => lazy val tryActual = Try(actual) BoolAlgebraM.fromEffect(run(tryActual.get)).flatMap { p => diff --git a/test/shared/src/main/scala/zio/test/Fun.scala b/test/shared/src/main/scala/zio/test/Fun.scala index 5933ec609909..4dfd32f3fba3 100644 --- a/test/shared/src/main/scala/zio/test/Fun.scala +++ b/test/shared/src/main/scala/zio/test/Fun.scala @@ -17,7 +17,7 @@ package zio.test import zio.internal.Executor -import zio.{ Runtime, ZIO } +import zio.{ Runtime, URIO, ZIO } /** * A `Fun[A, B]` is a referentially transparent version of a potentially @@ -45,7 +45,7 @@ private[test] object Fun { * Constructs a new `Fun` from an effectual function. The function should not * involve asynchronous effects. */ - def make[R, A, B](f: A => ZIO[R, Nothing, B]): ZIO[R, Nothing, Fun[A, B]] = + def make[R, A, B](f: A => URIO[R, B]): ZIO[R, Nothing, Fun[A, B]] = makeHash(f)(_.hashCode) /** @@ -53,7 +53,7 @@ private[test] object Fun { * This is useful when the domain of the function does not implement * `hashCode` in a way that is consistent with equality. */ - def makeHash[R, A, B](f: A => ZIO[R, Nothing, B])(hash: A => Int): ZIO[R, Nothing, Fun[A, B]] = + def makeHash[R, A, B](f: A => URIO[R, B])(hash: A => Int): ZIO[R, Nothing, Fun[A, B]] = ZIO.runtime[R].map { runtime => val funRuntime = withFunExecutor(runtime) Fun(a => funRuntime.unsafeRun(f(a)), hash) diff --git a/test/shared/src/main/scala/zio/test/Gen.scala b/test/shared/src/main/scala/zio/test/Gen.scala index 0a88625a7f0b..ef0aec3af6e6 100644 --- a/test/shared/src/main/scala/zio/test/Gen.scala +++ b/test/shared/src/main/scala/zio/test/Gen.scala @@ -23,7 +23,7 @@ import scala.math.Numeric.DoubleIsFractional import zio.random._ import zio.stream.{ Stream, ZStream } -import zio.{ Chunk, NonEmptyChunk, UIO, ZIO } +import zio.{ Chunk, NonEmptyChunk, UIO, URIO, ZIO } /** * A `Gen[R, A]` represents a generator of values of type `A`, which requires @@ -120,14 +120,14 @@ final case class Gen[-R, +A](sample: ZStream[R, Nothing, Sample[R, A]]) { self = * Runs the generator and collects all of its values in a list. */ def runCollect: ZIO[R, Nothing, List[A]] = - sample.map(_.value).runCollect + sample.map(_.value).runCollect.map(_.toList) /** * Repeatedly runs the generator and collects the specified number of values * in a list. */ def runCollectN(n: Int): ZIO[R, Nothing, List[A]] = - sample.map(_.value).forever.take(n.toLong).runCollect + sample.map(_.value).forever.take(n.toLong).runCollect.map(_.toList) /** * Runs the generator returning the first value of the generator. @@ -430,7 +430,7 @@ object Gen extends GenZIO with FunctionVariants with TimeVariants { /** * Constructs a generator from an effect that constructs a value. */ - def fromEffect[R, A](effect: ZIO[R, Nothing, A]): Gen[R, A] = + def fromEffect[R, A](effect: URIO[R, A]): Gen[R, A] = Gen(ZStream.fromEffect(effect.map(Sample.noShrink))) /** diff --git a/test/shared/src/main/scala/zio/test/GenZIO.scala b/test/shared/src/main/scala/zio/test/GenZIO.scala index 06b8d7eaec3a..801c19661148 100644 --- a/test/shared/src/main/scala/zio/test/GenZIO.scala +++ b/test/shared/src/main/scala/zio/test/GenZIO.scala @@ -82,13 +82,13 @@ trait GenZIO { /** * A generator of effects that have died with a `Throwable`. */ - final def died[R](gen: Gen[R, Throwable]): Gen[R, ZIO[Any, Nothing, Nothing]] = + final def died[R](gen: Gen[R, Throwable]): Gen[R, UIO[Nothing]] = gen.map(ZIO.die(_)) /** * A generator of effects that have failed with an error. */ - final def failures[R, E](gen: Gen[R, E]): Gen[R, ZIO[Any, E, Nothing]] = + final def failures[R, E](gen: Gen[R, E]): Gen[R, IO[E, Nothing]] = gen.map(ZIO.fail(_)) /** @@ -102,6 +102,6 @@ trait GenZIO { /** * A generator of successful effects. */ - final def successes[R, A](gen: Gen[R, A]): Gen[R, ZIO[Any, Nothing, A]] = + final def successes[R, A](gen: Gen[R, A]): Gen[R, UIO[A]] = gen.map(ZIO.succeedNow) } diff --git a/test/shared/src/main/scala/zio/test/TestAspect.scala b/test/shared/src/main/scala/zio/test/TestAspect.scala index a8e942dec240..6490d56d6075 100644 --- a/test/shared/src/main/scala/zio/test/TestAspect.scala +++ b/test/shared/src/main/scala/zio/test/TestAspect.scala @@ -178,7 +178,7 @@ object TestAspect extends TimeoutVariants { (_, _) => dump(label, fiber) *> fiber.join ) } - def dump[E, A](label: String, fiber: Fiber.Runtime[E, A]): ZIO[Live, Nothing, Unit] = + def dump[E, A](label: String, fiber: Fiber.Runtime[E, A]): URIO[Live, Unit] = Live.live(Fiber.putDumpStr(label, fiber)) spec.transform[R, TestFailure[E], TestSuccess] { case c @ Spec.SuiteCase(_, _, _) => c diff --git a/test/shared/src/main/scala/zio/test/TimeoutVariants.scala b/test/shared/src/main/scala/zio/test/TimeoutVariants.scala index 684b1315dff5..f91505bf6549 100644 --- a/test/shared/src/main/scala/zio/test/TimeoutVariants.scala +++ b/test/shared/src/main/scala/zio/test/TimeoutVariants.scala @@ -16,10 +16,10 @@ package zio.test -import zio.ZIO import zio.console import zio.duration._ import zio.test.environment.Live +import zio.{ URIO, ZIO } trait TimeoutVariants { @@ -62,7 +62,7 @@ trait TimeoutVariants { suiteLabels: List[String], testLabel: String, duration: Duration - ): ZIO[Live, Nothing, Unit] = + ): URIO[Live, Unit] = Live.live(console.putStrLn(renderWarning(suiteLabels, testLabel, duration))) private def renderWarning(suiteLabels: List[String], testLabel: String, duration: Duration): String = diff --git a/test/shared/src/main/scala/zio/test/environment/package.scala b/test/shared/src/main/scala/zio/test/environment/package.scala index 7a0e36b6176a..baffdc6ece58 100644 --- a/test/shared/src/main/scala/zio/test/environment/package.scala +++ b/test/shared/src/main/scala/zio/test/environment/package.scala @@ -478,7 +478,7 @@ package object environment extends PlatformSpecific { * time by the specified duration, running any actions scheduled for on or * before the new time in order. */ - def adjust(duration: => Duration): ZIO[TestClock, Nothing, Unit] = + def adjust(duration: => Duration): URIO[TestClock, Unit] = ZIO.accessM(_.get.adjust(duration)) /** @@ -494,7 +494,7 @@ package object environment extends PlatformSpecific { * time to the specified `OffsetDateTime`, running any actions scheduled * for on or before the new time in order. */ - def setDateTime(dateTime: => OffsetDateTime): ZIO[TestClock, Nothing, Unit] = + def setDateTime(dateTime: => OffsetDateTime): URIO[TestClock, Unit] = ZIO.accessM(_.get.setDateTime(dateTime)) /** @@ -502,7 +502,7 @@ package object environment extends PlatformSpecific { * time to the specified time in terms of duration since the epoch, * running any actions scheduled for on or before the new time in order. */ - def setTime(duration: => Duration): ZIO[TestClock, Nothing, Unit] = + def setTime(duration: => Duration): URIO[TestClock, Unit] = ZIO.accessM(_.get.setTime(duration)) /** @@ -511,7 +511,7 @@ package object environment extends PlatformSpecific { * since the epoch will not be altered and no scheduled actions will be * run as a result of this effect. */ - def setTimeZone(zone: => ZoneId): ZIO[TestClock, Nothing, Unit] = + def setTimeZone(zone: => ZoneId): URIO[TestClock, Unit] = ZIO.accessM(_.get.setTimeZone(zone)) /** @@ -525,7 +525,7 @@ package object environment extends PlatformSpecific { * Accesses a `TestClock` instance in the environment and returns the current * time zone. */ - val timeZone: ZIO[TestClock, Nothing, ZoneId] = + val timeZone: URIO[TestClock, ZoneId] = ZIO.accessM(_.get.timeZone) /** @@ -679,7 +679,7 @@ package object environment extends PlatformSpecific { * Takes the first value from the input buffer, if one exists, or else * fails with an `EOFException`. */ - val getStrLn: ZIO[Any, IOException, String] = { + val getStrLn: IO[IOException, String] = { for { input <- consoleState.get.flatMap(d => IO.fromOption(d.input.headOption) @@ -758,14 +758,14 @@ package object environment extends PlatformSpecific { * Accesses a `TestConsole` instance in the environment and clears the input * buffer. */ - val clearInput: ZIO[TestConsole, Nothing, Unit] = + val clearInput: URIO[TestConsole, Unit] = ZIO.accessM(_.get.clearInput) /** * Accesses a `TestConsole` instance in the environment and clears the output * buffer. */ - val clearOutput: ZIO[TestConsole, Nothing, Unit] = + val clearOutput: URIO[TestConsole, Unit] = ZIO.accessM(_.get.clearOutput) /** @@ -781,7 +781,7 @@ package object environment extends PlatformSpecific { * Accesses a `TestConsole` instance in the environment and writes the * specified sequence of strings to the input buffer. */ - def feedLines(lines: String*): ZIO[TestConsole, Nothing, Unit] = + def feedLines(lines: String*): URIO[TestConsole, Unit] = ZIO.accessM(_.get.feedLines(lines: _*)) /** @@ -1341,118 +1341,118 @@ package object environment extends PlatformSpecific { * Accesses a `TestRandom` instance in the environment and clears the buffer * of booleans. */ - val clearBooleans: ZIO[TestRandom, Nothing, Unit] = + val clearBooleans: URIO[TestRandom, Unit] = ZIO.accessM(_.get.clearBooleans) /** * Accesses a `TestRandom` instance in the environment and clears the buffer * of bytes. */ - val clearBytes: ZIO[TestRandom, Nothing, Unit] = + val clearBytes: URIO[TestRandom, Unit] = ZIO.accessM(_.get.clearBytes) /** * Accesses a `TestRandom` instance in the environment and clears the buffer * of characters. */ - val clearChars: ZIO[TestRandom, Nothing, Unit] = + val clearChars: URIO[TestRandom, Unit] = ZIO.accessM(_.get.clearChars) /** * Accesses a `TestRandom` instance in the environment and clears the buffer * of doubles. */ - val clearDoubles: ZIO[TestRandom, Nothing, Unit] = + val clearDoubles: URIO[TestRandom, Unit] = ZIO.accessM(_.get.clearDoubles) /** * Accesses a `TestRandom` instance in the environment and clears the buffer * of floats. */ - val clearFloats: ZIO[TestRandom, Nothing, Unit] = + val clearFloats: URIO[TestRandom, Unit] = ZIO.accessM(_.get.clearFloats) /** * Accesses a `TestRandom` instance in the environment and clears the buffer * of integers. */ - val clearInts: ZIO[TestRandom, Nothing, Unit] = + val clearInts: URIO[TestRandom, Unit] = ZIO.accessM(_.get.clearInts) /** * Accesses a `TestRandom` instance in the environment and clears the buffer * of longs. */ - val clearLongs: ZIO[TestRandom, Nothing, Unit] = + val clearLongs: URIO[TestRandom, Unit] = ZIO.accessM(_.get.clearLongs) /** * Accesses a `TestRandom` instance in the environment and clears the buffer * of strings. */ - val clearStrings: ZIO[TestRandom, Nothing, Unit] = + val clearStrings: URIO[TestRandom, Unit] = ZIO.accessM(_.get.clearStrings) /** * Accesses a `TestRandom` instance in the environment and feeds the buffer * with the specified sequence of booleans. */ - def feedBooleans(booleans: Boolean*): ZIO[TestRandom, Nothing, Unit] = + def feedBooleans(booleans: Boolean*): URIO[TestRandom, Unit] = ZIO.accessM(_.get.feedBooleans(booleans: _*)) /** * Accesses a `TestRandom` instance in the environment and feeds the buffer * with the specified sequence of chunks of bytes. */ - def feedBytes(bytes: Chunk[Byte]*): ZIO[TestRandom, Nothing, Unit] = + def feedBytes(bytes: Chunk[Byte]*): URIO[TestRandom, Unit] = ZIO.accessM(_.get.feedBytes(bytes: _*)) /** * Accesses a `TestRandom` instance in the environment and feeds the buffer * with the specified sequence of characters. */ - def feedChars(chars: Char*): ZIO[TestRandom, Nothing, Unit] = + def feedChars(chars: Char*): URIO[TestRandom, Unit] = ZIO.accessM(_.get.feedChars(chars: _*)) /** * Accesses a `TestRandom` instance in the environment and feeds the buffer * with the specified sequence of doubles. */ - def feedDoubles(doubles: Double*): ZIO[TestRandom, Nothing, Unit] = + def feedDoubles(doubles: Double*): URIO[TestRandom, Unit] = ZIO.accessM(_.get.feedDoubles(doubles: _*)) /** * Accesses a `TestRandom` instance in the environment and feeds the buffer * with the specified sequence of floats. */ - def feedFloats(floats: Float*): ZIO[TestRandom, Nothing, Unit] = + def feedFloats(floats: Float*): URIO[TestRandom, Unit] = ZIO.accessM(_.get.feedFloats(floats: _*)) /** * Accesses a `TestRandom` instance in the environment and feeds the buffer * with the specified sequence of integers. */ - def feedInts(ints: Int*): ZIO[TestRandom, Nothing, Unit] = + def feedInts(ints: Int*): URIO[TestRandom, Unit] = ZIO.accessM(_.get.feedInts(ints: _*)) /** * Accesses a `TestRandom` instance in the environment and feeds the buffer * with the specified sequence of longs. */ - def feedLongs(longs: Long*): ZIO[TestRandom, Nothing, Unit] = + def feedLongs(longs: Long*): URIO[TestRandom, Unit] = ZIO.accessM(_.get.feedLongs(longs: _*)) /** * Accesses a `TestRandom` instance in the environment and feeds the buffer * with the specified sequence of strings. */ - def feedStrings(strings: String*): ZIO[TestRandom, Nothing, Unit] = + def feedStrings(strings: String*): URIO[TestRandom, Unit] = ZIO.accessM(_.get.feedStrings(strings: _*)) /** * Accesses a `TestRandom` instance in the environment and gets the seed. */ - val getSeed: ZIO[TestRandom, Nothing, Long] = + val getSeed: URIO[TestRandom, Long] = ZIO.accessM(_.get.getSeed) /** @@ -1507,7 +1507,7 @@ package object environment extends PlatformSpecific { * Accesses a `TestRandom` instance in the environment and sets the seed to * the specified value. */ - def setSeed(seed: => Long): ZIO[TestRandom, Nothing, Unit] = + def setSeed(seed: => Long): URIO[TestRandom, Unit] = ZIO.accessM(_.get.setSeed(seed)) /** @@ -1581,7 +1581,7 @@ package object environment extends PlatformSpecific { /** * Returns the system line separator. */ - val lineSeparator: ZIO[Any, Nothing, String] = + val lineSeparator: UIO[String] = systemState.get.map(_.lineSeparator) val properties: ZIO[Any, Throwable, Map[String, String]] = @@ -1677,14 +1677,14 @@ package object environment extends PlatformSpecific { * Accesses a `TestSystem` instance in the environment and adds the specified * name and value to the mapping of environment variables. */ - def putEnv(name: => String, value: => String): ZIO[TestSystem, Nothing, Unit] = + def putEnv(name: => String, value: => String): URIO[TestSystem, Unit] = ZIO.accessM(_.get.putEnv(name, value)) /** * Accesses a `TestSystem` instance in the environment and adds the specified * name and value to the mapping of system properties. */ - def putProperty(name: => String, value: => String): ZIO[TestSystem, Nothing, Unit] = + def putProperty(name: => String, value: => String): URIO[TestSystem, Unit] = ZIO.accessM(_.get.putProperty(name, value)) /** @@ -1698,21 +1698,21 @@ package object environment extends PlatformSpecific { * Accesses a `TestSystem` instance in the environment and sets the line * separator to the specified value. */ - def setLineSeparator(lineSep: => String): ZIO[TestSystem, Nothing, Unit] = + def setLineSeparator(lineSep: => String): URIO[TestSystem, Unit] = ZIO.accessM(_.get.setLineSeparator(lineSep)) /** * Accesses a `TestSystem` instance in the environment and clears the mapping * of environment variables. */ - def clearEnv(variable: => String): ZIO[TestSystem, Nothing, Unit] = + def clearEnv(variable: => String): URIO[TestSystem, Unit] = ZIO.accessM(_.get.clearEnv(variable)) /** * Accesses a `TestSystem` instance in the environment and clears the mapping * of system properties. */ - def clearProperty(prop: => String): ZIO[TestSystem, Nothing, Unit] = + def clearProperty(prop: => String): URIO[TestSystem, Unit] = ZIO.accessM(_.get.clearProperty(prop)) /** diff --git a/test/shared/src/main/scala/zio/test/laws/ZLaws.scala b/test/shared/src/main/scala/zio/test/laws/ZLaws.scala index 074a9750b6db..1eda5b91edc3 100644 --- a/test/shared/src/main/scala/zio/test/laws/ZLaws.scala +++ b/test/shared/src/main/scala/zio/test/laws/ZLaws.scala @@ -16,8 +16,8 @@ package zio.test.laws -import zio.ZIO import zio.test.{ check, checkM, Gen, TestResult } +import zio.{ URIO, ZIO } /** * `ZLaws[Caps, R]` represents a set of laws that values with capabilities @@ -54,7 +54,7 @@ object ZLaws { */ abstract class Law1[-Caps[_]](label: String) extends ZLaws[Caps, Any] { self => def apply[A: Caps](a1: A): TestResult - final def run[R, A: Caps](gen: Gen[R, A]): ZIO[R, Nothing, TestResult] = + final def run[R, A: Caps](gen: Gen[R, A]): URIO[R, TestResult] = check(gen)(apply(_).map(_.label(label))) } @@ -62,7 +62,7 @@ object ZLaws { * Constructs a law from an effectual function taking a single parameter. */ abstract class Law1M[-Caps[_], -R](label: String) extends ZLaws[Caps, R] { self => - def apply[A: Caps](a1: A): ZIO[R, Nothing, TestResult] + def apply[A: Caps](a1: A): URIO[R, TestResult] final def run[R1 <: R, A: Caps](gen: Gen[R1, A]): ZIO[R1, Nothing, TestResult] = checkM(gen)(apply(_).map(_.map(_.label(label)))) } @@ -72,7 +72,7 @@ object ZLaws { */ abstract class Law2[-Caps[_]](label: String) extends ZLaws[Caps, Any] { self => def apply[A: Caps](a1: A, a2: A): TestResult - final def run[R, A: Caps](gen: Gen[R, A]): ZIO[R, Nothing, TestResult] = + final def run[R, A: Caps](gen: Gen[R, A]): URIO[R, TestResult] = check(gen, gen)(apply(_, _).map(_.label(label))) } @@ -80,7 +80,7 @@ object ZLaws { * Constructs a law from an effectual function taking two parameters. */ abstract class Law2M[-Caps[_], -R](label: String) extends ZLaws[Caps, R] { self => - def apply[A: Caps](a1: A, a2: A): ZIO[R, Nothing, TestResult] + def apply[A: Caps](a1: A, a2: A): URIO[R, TestResult] final def run[R1 <: R, A: Caps](gen: Gen[R1, A]): ZIO[R1, Nothing, TestResult] = checkM(gen, gen)(apply(_, _).map(_.map(_.label(label)))) } @@ -90,7 +90,7 @@ object ZLaws { */ abstract class Law3[-Caps[_]](label: String) extends ZLaws[Caps, Any] { self => def apply[A: Caps](a1: A, a2: A, a3: A): TestResult - final def run[R, A: Caps](gen: Gen[R, A]): ZIO[R, Nothing, TestResult] = + final def run[R, A: Caps](gen: Gen[R, A]): URIO[R, TestResult] = check(gen, gen, gen)(apply(_, _, _).map(_.label(label))) } @@ -98,7 +98,7 @@ object ZLaws { * Constructs a law from an effectual function taking three parameters. */ abstract class Law3M[-Caps[_], -R](label: String) extends ZLaws[Caps, R] { self => - def apply[A: Caps](a1: A, a2: A, a3: A): ZIO[R, Nothing, TestResult] + def apply[A: Caps](a1: A, a2: A, a3: A): URIO[R, TestResult] final def run[R1 <: R, A: Caps](gen: Gen[R1, A]): ZIO[R1, Nothing, TestResult] = checkM(gen, gen, gen)(apply(_, _, _).map(_.map(_.label(label)))) } diff --git a/test/shared/src/main/scala/zio/test/laws/ZLaws2.scala b/test/shared/src/main/scala/zio/test/laws/ZLaws2.scala index a80548b80836..85c6ff274a43 100644 --- a/test/shared/src/main/scala/zio/test/laws/ZLaws2.scala +++ b/test/shared/src/main/scala/zio/test/laws/ZLaws2.scala @@ -16,8 +16,8 @@ package zio.test.laws -import zio.ZIO import zio.test.{ check, Gen, TestResult } +import zio.{ URIO, ZIO } trait ZLaws2[-CapsBoth[_, _], -CapsLeft[_], -CapsRight[_], -R] { self => @@ -48,7 +48,7 @@ object ZLaws2 { def apply[A: CapsLeft, B: CapsRight](a1: A)(implicit CapsBoth: CapsBoth[A, B]): TestResult final def run[R, A: CapsLeft, B: CapsRight](a: Gen[R, A], b: Gen[R, B])( implicit CapsBoth: CapsBoth[A, B] - ): ZIO[R, Nothing, TestResult] = + ): URIO[R, TestResult] = check(a, b)((a, _) => apply(a).map(_.label(label))) } @@ -57,7 +57,7 @@ object ZLaws2 { def apply[A: CapsLeft, B: CapsRight](b1: B)(implicit CapsBoth: CapsBoth[A, B]): TestResult final def run[R, A: CapsLeft, B: CapsRight](a: Gen[R, A], b: Gen[R, B])( implicit CapsBoth: CapsBoth[A, B] - ): ZIO[R, Nothing, TestResult] = + ): URIO[R, TestResult] = check(a, b)((_, b) => apply(b).map(_.label(label))) } } diff --git a/test/shared/src/main/scala/zio/test/laws/ZLawsF.scala b/test/shared/src/main/scala/zio/test/laws/ZLawsF.scala index b904967d722d..95374fda1b49 100644 --- a/test/shared/src/main/scala/zio/test/laws/ZLawsF.scala +++ b/test/shared/src/main/scala/zio/test/laws/ZLawsF.scala @@ -16,8 +16,8 @@ package zio.test.laws -import zio.ZIO import zio.test.{ check, checkM, Gen, TestResult } +import zio.{ URIO, ZIO } /** * `ZLaws[CapsF, Caps, R]` describes a set of laws that a parameterized type @@ -68,7 +68,7 @@ object ZLawsF { */ abstract class ComposeLaw[-CapsF[_[+_]], -Caps[_]](label: String) extends Covariant[CapsF, Caps, Any] { self => def apply[F[+_]: CapsF, A: Caps, B: Caps, C: Caps](fa: F[A], f: A => B, g: B => C): TestResult - final def run[R, F[+_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): ZIO[R, Nothing, TestResult] = + final def run[R, F[+_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): URIO[R, TestResult] = check(genF(gen), Gen.function(gen), Gen.function(gen))(apply(_, _, _).map(_.label(label))) } @@ -78,7 +78,7 @@ object ZLawsF { */ abstract class FlattenLaw[-CapsF[_[+_]], -Caps[_]](label: String) extends Covariant[CapsF, Caps, Any] { self => def apply[F[+_]: CapsF, A: Caps](fffa: F[F[F[A]]]): TestResult - final def run[R, F[+_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): ZIO[R, Nothing, TestResult] = + final def run[R, F[+_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): URIO[R, TestResult] = check(genF(genF(genF(gen))))(apply(_).map(_.label(label))) } @@ -87,7 +87,7 @@ object ZLawsF { */ abstract class Law1[-CapsF[_[+_]], -Caps[_]](label: String) extends Covariant[CapsF, Caps, Any] { self => def apply[F[+_]: CapsF, A: Caps](fa: F[A]): TestResult - final def run[R, F[+_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): ZIO[R, Nothing, TestResult] = + final def run[R, F[+_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): URIO[R, TestResult] = check(genF(gen))(apply(_).map(_.label(label))) } @@ -95,7 +95,7 @@ object ZLawsF { * Constructs a law from an effectual function taking a single parameter. */ abstract class Law1M[-CapsF[_[+_]], -Caps[_], -R](label: String) extends Covariant[CapsF, Caps, R] { self => - def apply[F[+_]: CapsF, A: Caps](fa: F[A]): ZIO[R, Nothing, TestResult] + def apply[F[+_]: CapsF, A: Caps](fa: F[A]): URIO[R, TestResult] final def run[R1 <: R, F[+_]: CapsF, A: Caps](genF: GenF[R1, F], gen: Gen[R1, A]): ZIO[R1, Nothing, TestResult] = checkM(genF(gen))(apply(_).map(_.map(_.label(label)))) } @@ -105,7 +105,7 @@ object ZLawsF { */ abstract class Law2[-CapsF[_[+_]], -Caps[_]](label: String) extends Covariant[CapsF, Caps, Any] { self => def apply[F[+_]: CapsF, A: Caps, B: Caps](fa: F[A], fb: F[B]): TestResult - final def run[R, F[+_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): ZIO[R, Nothing, TestResult] = + final def run[R, F[+_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): URIO[R, TestResult] = check(genF(gen), genF(gen))(apply(_, _).map(_.label(label))) } @@ -113,7 +113,7 @@ object ZLawsF { * Constructs a law from an effectual function taking two parameters. */ abstract class Law2M[-CapsF[_[+_]], -Caps[_], -R](label: String) extends Covariant[CapsF, Caps, R] { self => - def apply[F[+_]: CapsF, A: Caps, B: Caps](fa: F[A], fb: F[B]): ZIO[R, Nothing, TestResult] + def apply[F[+_]: CapsF, A: Caps, B: Caps](fa: F[A], fb: F[B]): URIO[R, TestResult] final def run[R1 <: R, F[+_]: CapsF, A: Caps](genF: GenF[R1, F], gen: Gen[R1, A]): ZIO[R1, Nothing, TestResult] = checkM(genF(gen), genF(gen))(apply(_, _).map(_.map(_.label(label)))) } @@ -123,7 +123,7 @@ object ZLawsF { */ abstract class Law3[-CapsF[_[+_]], -Caps[_]](label: String) extends Covariant[CapsF, Caps, Any] { self => def apply[F[+_]: CapsF, A: Caps, B: Caps, C: Caps](fa: F[A], fb: F[B], fc: F[C]): TestResult - final def run[R, F[+_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): ZIO[R, Nothing, TestResult] = + final def run[R, F[+_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): URIO[R, TestResult] = check(genF(gen), genF(gen), genF(gen))(apply(_, _, _).map(_.label(label))) } @@ -131,7 +131,7 @@ object ZLawsF { * Constructs a law from an effectual function taking three parameters. */ abstract class Law3M[-CapsF[_[+_]], -Caps[_], -R](label: String) extends Covariant[CapsF, Caps, R] { self => - def apply[F[+_]: CapsF, A: Caps, B: Caps, C: Caps](fa: F[A], fb: F[B], fc: F[C]): ZIO[R, Nothing, TestResult] + def apply[F[+_]: CapsF, A: Caps, B: Caps, C: Caps](fa: F[A], fb: F[B], fc: F[C]): URIO[R, TestResult] final def run[R1 <: R, F[+_]: CapsF, A: Caps](genF: GenF[R1, F], gen: Gen[R1, A]): ZIO[R1, Nothing, TestResult] = checkM(genF(gen), genF(gen), genF(gen))(apply(_, _, _).map(_.map(_.label(label)))) } @@ -175,9 +175,9 @@ object ZLawsF { */ abstract class ComposeLaw[-CapsF[_[-_]], -Caps[_]](label: String) extends Contravariant[CapsF, Caps, Any] { self => - def apply[F[-_]: CapsF, A: Caps, B: Caps, C: Caps](fa: F[A], f: A => B, g: B => C): TestResult - final def run[R, F[-_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): ZIO[R, Nothing, TestResult] = - check(genF(gen), Gen.function(gen), Gen.function(gen))(apply(_, _, _).map(_.label(label))) + def apply[F[-_]: CapsF, A: Caps, B: Caps, C: Caps](fa: F[A], f: B => A, g: C => B): TestResult + final def run[R, F[-_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): URIO[R, TestResult] = + check(genF(gen), Gen.function[R, A, A](gen), Gen.function[R, A, A](gen))(apply(_, _, _).map(_.label(label))) } /** @@ -185,7 +185,7 @@ object ZLawsF { */ abstract class Law1[-CapsF[_[-_]], -Caps[_]](label: String) extends Contravariant[CapsF, Caps, Any] { self => def apply[F[-_]: CapsF, A: Caps](fa: F[A]): TestResult - final def run[R, F[-_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): ZIO[R, Nothing, TestResult] = + final def run[R, F[-_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): URIO[R, TestResult] = check(genF(gen))(apply(_).map(_.label(label))) } @@ -193,7 +193,7 @@ object ZLawsF { * Constructs a law from an effectual function taking a single parameter. */ abstract class Law1M[-CapsF[_[-_]], -Caps[_], -R](label: String) extends Contravariant[CapsF, Caps, R] { self => - def apply[F[-_]: CapsF, A: Caps](fa: F[A]): ZIO[R, Nothing, TestResult] + def apply[F[-_]: CapsF, A: Caps](fa: F[A]): URIO[R, TestResult] final def run[R1 <: R, F[-_]: CapsF, A: Caps](genF: GenF[R1, F], gen: Gen[R1, A]): ZIO[R1, Nothing, TestResult] = checkM(genF(gen))(apply(_).map(_.map(_.label(label)))) } @@ -203,7 +203,7 @@ object ZLawsF { */ abstract class Law2[-CapsF[_[-_]], -Caps[_]](label: String) extends Contravariant[CapsF, Caps, Any] { self => def apply[F[-_]: CapsF, A: Caps, B: Caps](fa: F[A], fb: F[B]): TestResult - final def run[R, F[-_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): ZIO[R, Nothing, TestResult] = + final def run[R, F[-_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): URIO[R, TestResult] = check(genF(gen), genF(gen))(apply(_, _).map(_.label(label))) } @@ -211,7 +211,7 @@ object ZLawsF { * Constructs a law from an effectual function taking two parameters. */ abstract class Law2M[-CapsF[_[-_]], -Caps[_], -R](label: String) extends Contravariant[CapsF, Caps, R] { self => - def apply[F[-_]: CapsF, A: Caps, B: Caps](fa: F[A], fb: F[B]): ZIO[R, Nothing, TestResult] + def apply[F[-_]: CapsF, A: Caps, B: Caps](fa: F[A], fb: F[B]): URIO[R, TestResult] final def run[R1 <: R, F[-_]: CapsF, A: Caps](genF: GenF[R1, F], gen: Gen[R1, A]): ZIO[R1, Nothing, TestResult] = checkM(genF(gen), genF(gen))(apply(_, _).map(_.map(_.label(label)))) } @@ -221,7 +221,7 @@ object ZLawsF { */ abstract class Law3[-CapsF[_[-_]], -Caps[_]](label: String) extends Contravariant[CapsF, Caps, Any] { self => def apply[F[-_]: CapsF, A: Caps, B: Caps, C: Caps](fa: F[A], fb: F[B], fc: F[C]): TestResult - final def run[R, F[-_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): ZIO[R, Nothing, TestResult] = + final def run[R, F[-_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): URIO[R, TestResult] = check(genF(gen), genF(gen), genF(gen))(apply(_, _, _).map(_.label(label))) } @@ -229,7 +229,7 @@ object ZLawsF { * Constructs a law from an effectual function taking three parameters. */ abstract class Law3M[-CapsF[_[-_]], -Caps[_], -R](label: String) extends Contravariant[CapsF, Caps, R] { self => - def apply[F[-_]: CapsF, A: Caps, B: Caps, C: Caps](fa: F[A], fb: F[B], fc: F[C]): ZIO[R, Nothing, TestResult] + def apply[F[-_]: CapsF, A: Caps, B: Caps, C: Caps](fa: F[A], fb: F[B], fc: F[C]): URIO[R, TestResult] final def run[R1 <: R, F[-_]: CapsF, A: Caps](genF: GenF[R1, F], gen: Gen[R1, A]): ZIO[R1, Nothing, TestResult] = checkM(genF(gen), genF(gen), genF(gen))(apply(_, _, _).map(_.map(_.label(label)))) } @@ -272,7 +272,7 @@ object ZLawsF { */ abstract class Law1[-CapsF[_[_]], -Caps[_]](label: String) extends Invariant[CapsF, Caps, Any] { self => def apply[F[_]: CapsF, A: Caps](fa: F[A]): TestResult - final def run[R, F[_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): ZIO[R, Nothing, TestResult] = + final def run[R, F[_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): URIO[R, TestResult] = check(genF(gen))(apply(_).map(_.label(label))) } @@ -280,7 +280,7 @@ object ZLawsF { * Constructs a law from an effectual function taking a single parameter. */ abstract class Law1M[-CapsF[_[_]], -Caps[_], -R](label: String) extends Invariant[CapsF, Caps, R] { self => - def apply[F[_]: CapsF, A: Caps](fa: F[A]): ZIO[R, Nothing, TestResult] + def apply[F[_]: CapsF, A: Caps](fa: F[A]): URIO[R, TestResult] final def run[R1 <: R, F[_]: CapsF, A: Caps](genF: GenF[R1, F], gen: Gen[R1, A]): ZIO[R1, Nothing, TestResult] = checkM(genF(gen))(apply(_).map(_.map(_.label(label)))) } @@ -290,7 +290,7 @@ object ZLawsF { */ abstract class Law2[-CapsF[_[_]], -Caps[_]](label: String) extends Invariant[CapsF, Caps, Any] { self => def apply[F[_]: CapsF, A: Caps, B: Caps](fa: F[A], fb: F[B]): TestResult - final def run[R, F[_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): ZIO[R, Nothing, TestResult] = + final def run[R, F[_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): URIO[R, TestResult] = check(genF(gen), genF(gen))(apply(_, _).map(_.label(label))) } @@ -298,7 +298,7 @@ object ZLawsF { * Constructs a law from an effectual function taking two parameters. */ abstract class Law2M[-CapsF[_[_]], -Caps[_], -R](label: String) extends Invariant[CapsF, Caps, R] { self => - def apply[F[_]: CapsF, A: Caps, B: Caps](fa: F[A], fb: F[B]): ZIO[R, Nothing, TestResult] + def apply[F[_]: CapsF, A: Caps, B: Caps](fa: F[A], fb: F[B]): URIO[R, TestResult] final def run[R1 <: R, F[_]: CapsF, A: Caps](genF: GenF[R1, F], gen: Gen[R1, A]): ZIO[R1, Nothing, TestResult] = checkM(genF(gen), genF(gen))(apply(_, _).map(_.map(_.label(label)))) } @@ -308,7 +308,7 @@ object ZLawsF { */ abstract class Law3[-CapsF[_[_]], -Caps[_]](label: String) extends Invariant[CapsF, Caps, Any] { self => def apply[F[_]: CapsF, A: Caps, B: Caps, C: Caps](fa: F[A], fb: F[B], fc: F[C]): TestResult - final def run[R, F[_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): ZIO[R, Nothing, TestResult] = + final def run[R, F[_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): URIO[R, TestResult] = check(genF(gen), genF(gen), genF(gen))(apply(_, _, _).map(_.label(label))) } @@ -316,7 +316,7 @@ object ZLawsF { * Constructs a law from an effectual function taking three parameters. */ abstract class Law3M[-CapsF[_[_]], -Caps[_], -R](label: String) extends Invariant[CapsF, Caps, R] { self => - def apply[F[_]: CapsF, A: Caps, B: Caps, C: Caps](fa: F[A], fb: F[B], fc: F[C]): ZIO[R, Nothing, TestResult] + def apply[F[_]: CapsF, A: Caps, B: Caps, C: Caps](fa: F[A], fb: F[B], fc: F[C]): URIO[R, TestResult] final def run[R1 <: R, F[_]: CapsF, A: Caps](genF: GenF[R1, F], gen: Gen[R1, A]): ZIO[R1, Nothing, TestResult] = checkM(genF(gen), genF(gen), genF(gen))(apply(_, _, _).map(_.map(_.label(label)))) } diff --git a/test/shared/src/main/scala/zio/test/laws/package.scala b/test/shared/src/main/scala/zio/test/laws/package.scala index 290084ca3b63..88a2f0e3d8e3 100644 --- a/test/shared/src/main/scala/zio/test/laws/package.scala +++ b/test/shared/src/main/scala/zio/test/laws/package.scala @@ -99,21 +99,24 @@ package object laws { type Invariant[-CapsF[_[_]], -Caps[_]] = ZLawsF.Invariant[CapsF, Caps, Any] object Covariant { - type Law1[-CapsF[_[+_]], -Caps[_]] = ZLawsF.Covariant.Law1[CapsF, Caps] - type Law1M[-CapsF[_[+_]], -Caps[_]] = ZLawsF.Covariant.Law1M[CapsF, Caps, Any] - type Law2[-CapsF[_[+_]], -Caps[_]] = ZLawsF.Covariant.Law2[CapsF, Caps] - type Law2M[-CapsF[_[+_]], -Caps[_]] = ZLawsF.Covariant.Law2M[CapsF, Caps, Any] - type Law3[-CapsF[_[+_]], -Caps[_]] = ZLawsF.Covariant.Law3[CapsF, Caps] - type Law3M[-CapsF[_[+_]], -Caps[_]] = ZLawsF.Covariant.Law3M[CapsF, Caps, Any] + type ComposeLaw[-CapsF[_[+_]], -Caps[_]] = ZLawsF.Covariant.ComposeLaw[CapsF, Caps] + type FlattenLaw[-CapsF[_[+_]], -Caps[_]] = ZLawsF.Covariant.FlattenLaw[CapsF, Caps] + type Law1[-CapsF[_[+_]], -Caps[_]] = ZLawsF.Covariant.Law1[CapsF, Caps] + type Law1M[-CapsF[_[+_]], -Caps[_]] = ZLawsF.Covariant.Law1M[CapsF, Caps, Any] + type Law2[-CapsF[_[+_]], -Caps[_]] = ZLawsF.Covariant.Law2[CapsF, Caps] + type Law2M[-CapsF[_[+_]], -Caps[_]] = ZLawsF.Covariant.Law2M[CapsF, Caps, Any] + type Law3[-CapsF[_[+_]], -Caps[_]] = ZLawsF.Covariant.Law3[CapsF, Caps] + type Law3M[-CapsF[_[+_]], -Caps[_]] = ZLawsF.Covariant.Law3M[CapsF, Caps, Any] } object Contravariant { - type Law1[-CapsF[_[-_]], -Caps[_]] = ZLawsF.Contravariant.Law1[CapsF, Caps] - type Law1M[-CapsF[_[-_]], -Caps[_]] = ZLawsF.Contravariant.Law1M[CapsF, Caps, Any] - type Law2[-CapsF[_[-_]], -Caps[_]] = ZLawsF.Contravariant.Law2[CapsF, Caps] - type Law2M[-CapsF[_[-_]], -Caps[_]] = ZLawsF.Contravariant.Law2M[CapsF, Caps, Any] - type Law3[-CapsF[_[-_]], -Caps[_]] = ZLawsF.Contravariant.Law3[CapsF, Caps] - type Law3M[-CapsF[_[-_]], -Caps[_]] = ZLawsF.Contravariant.Law3M[CapsF, Caps, Any] + type ComposeLaw[-CapsF[_[-_]], -Caps[_]] = ZLawsF.Contravariant.ComposeLaw[CapsF, Caps] + type Law1[-CapsF[_[-_]], -Caps[_]] = ZLawsF.Contravariant.Law1[CapsF, Caps] + type Law1M[-CapsF[_[-_]], -Caps[_]] = ZLawsF.Contravariant.Law1M[CapsF, Caps, Any] + type Law2[-CapsF[_[-_]], -Caps[_]] = ZLawsF.Contravariant.Law2[CapsF, Caps] + type Law2M[-CapsF[_[-_]], -Caps[_]] = ZLawsF.Contravariant.Law2M[CapsF, Caps, Any] + type Law3[-CapsF[_[-_]], -Caps[_]] = ZLawsF.Contravariant.Law3[CapsF, Caps] + type Law3M[-CapsF[_[-_]], -Caps[_]] = ZLawsF.Contravariant.Law3M[CapsF, Caps, Any] } object Invariant { diff --git a/test/shared/src/main/scala/zio/test/package.scala b/test/shared/src/main/scala/zio/test/package.scala index 31aceea1cfb0..d0de244c010c 100644 --- a/test/shared/src/main/scala/zio/test/package.scala +++ b/test/shared/src/main/scala/zio/test/package.scala @@ -176,13 +176,13 @@ package object test extends CompileVariants { * Checks the test passes for "sufficient" numbers of samples from the * given random variable. */ - def check[R, A](rv: Gen[R, A])(test: A => TestResult): ZIO[R, Nothing, TestResult] = + def check[R, A](rv: Gen[R, A])(test: A => TestResult): URIO[R, TestResult] = checkN(200)(rv)(test) /** * A version of `check` that accepts two random variables. */ - def check[R, A, B](rv1: Gen[R, A], rv2: Gen[R, B])(test: (A, B) => TestResult): ZIO[R, Nothing, TestResult] = + def check[R, A, B](rv1: Gen[R, A], rv2: Gen[R, B])(test: (A, B) => TestResult): URIO[R, TestResult] = check(rv1 <*> rv2)(test.tupled) /** @@ -190,7 +190,7 @@ package object test extends CompileVariants { */ def check[R, A, B, C](rv1: Gen[R, A], rv2: Gen[R, B], rv3: Gen[R, C])( test: (A, B, C) => TestResult - ): ZIO[R, Nothing, TestResult] = + ): URIO[R, TestResult] = check(rv1 <*> rv2 <*> rv3)(reassociate(test)) /** @@ -198,7 +198,7 @@ package object test extends CompileVariants { */ def check[R, A, B, C, D](rv1: Gen[R, A], rv2: Gen[R, B], rv3: Gen[R, C], rv4: Gen[R, D])( test: (A, B, C, D) => TestResult - ): ZIO[R, Nothing, TestResult] = + ): URIO[R, TestResult] = check(rv1 <*> rv2 <*> rv3 <*> rv4)(reassociate(test)) /** @@ -206,7 +206,7 @@ package object test extends CompileVariants { */ def check[R, A, B, C, D, F](rv1: Gen[R, A], rv2: Gen[R, B], rv3: Gen[R, C], rv4: Gen[R, D], rv5: Gen[R, F])( test: (A, B, C, D, F) => TestResult - ): ZIO[R, Nothing, TestResult] = + ): URIO[R, TestResult] = check(rv1 <*> rv2 <*> rv3 <*> rv4 <*> rv5)(reassociate(test)) /** @@ -221,7 +221,7 @@ package object test extends CompileVariants { rv6: Gen[R, G] )( test: (A, B, C, D, F, G) => TestResult - ): ZIO[R, Nothing, TestResult] = + ): URIO[R, TestResult] = check(rv1 <*> rv2 <*> rv3 <*> rv4 <*> rv5 <*> rv6)(reassociate(test)) /** @@ -289,13 +289,13 @@ package object test extends CompileVariants { * is useful for deterministic `Gen` that comprehensively explore all * possibilities in a given domain. */ - def checkAll[R, A](rv: Gen[R, A])(test: A => TestResult): ZIO[R, Nothing, TestResult] = + def checkAll[R, A](rv: Gen[R, A])(test: A => TestResult): URIO[R, TestResult] = checkAllM(rv)(test andThen ZIO.succeedNow) /** * A version of `checkAll` that accepts two random variables. */ - def checkAll[R, A, B](rv1: Gen[R, A], rv2: Gen[R, B])(test: (A, B) => TestResult): ZIO[R, Nothing, TestResult] = + def checkAll[R, A, B](rv1: Gen[R, A], rv2: Gen[R, B])(test: (A, B) => TestResult): URIO[R, TestResult] = checkAll(rv1 <*> rv2)(test.tupled) /** @@ -303,7 +303,7 @@ package object test extends CompileVariants { */ def checkAll[R, A, B, C](rv1: Gen[R, A], rv2: Gen[R, B], rv3: Gen[R, C])( test: (A, B, C) => TestResult - ): ZIO[R, Nothing, TestResult] = + ): URIO[R, TestResult] = checkAll(rv1 <*> rv2 <*> rv3)(reassociate(test)) /** @@ -311,7 +311,7 @@ package object test extends CompileVariants { */ def checkAll[R, A, B, C, D](rv1: Gen[R, A], rv2: Gen[R, B], rv3: Gen[R, C], rv4: Gen[R, D])( test: (A, B, C, D) => TestResult - ): ZIO[R, Nothing, TestResult] = + ): URIO[R, TestResult] = checkAll(rv1 <*> rv2 <*> rv3 <*> rv4)(reassociate(test)) /** @@ -319,7 +319,7 @@ package object test extends CompileVariants { */ def checkAll[R, A, B, C, D, F](rv1: Gen[R, A], rv2: Gen[R, B], rv3: Gen[R, C], rv4: Gen[R, D], rv5: Gen[R, F])( test: (A, B, C, D, F) => TestResult - ): ZIO[R, Nothing, TestResult] = + ): URIO[R, TestResult] = checkAll(rv1 <*> rv2 <*> rv3 <*> rv4 <*> rv5)(reassociate(test)) /** @@ -334,7 +334,7 @@ package object test extends CompileVariants { rv6: Gen[R, G] )( test: (A, B, C, D, F, G) => TestResult - ): ZIO[R, Nothing, TestResult] = + ): URIO[R, TestResult] = checkAll(rv1 <*> rv2 <*> rv3 <*> rv4 <*> rv5 <*> rv6)(reassociate(test)) /** @@ -427,7 +427,7 @@ package object test extends CompileVariants { /** * Creates an ignored test result. */ - val ignored: ZIO[Any, Nothing, TestSuccess] = + val ignored: UIO[TestSuccess] = ZIO.succeedNow(TestSuccess.Ignored) /** @@ -488,14 +488,14 @@ package object test extends CompileVariants { * Accesses an `Annotations` instance in the environment and appends the * specified annotation to the annotation map. */ - def annotate[V](key: TestAnnotation[V], value: V): ZIO[Annotations, Nothing, Unit] = + def annotate[V](key: TestAnnotation[V], value: V): URIO[Annotations, Unit] = ZIO.accessM(_.get.annotate(key, value)) /** * Accesses an `Annotations` instance in the environment and retrieves the * annotation of the specified type, or its default value if there is none. */ - def get[V](key: TestAnnotation[V]): ZIO[Annotations, Nothing, V] = + def get[V](key: TestAnnotation[V]): URIO[Annotations, V] = ZIO.accessM(_.get.get(key)) /** @@ -540,7 +540,7 @@ package object test extends CompileVariants { } }) - def size: ZIO[Sized, Nothing, Int] = + def size: URIO[Sized, Int] = ZIO.accessM[Sized](_.get.size) def withSize[R <: Sized, E, A](size: Int)(zio: ZIO[R, E, A]): ZIO[R, E, A] = @@ -566,21 +566,21 @@ package object test extends CompileVariants { object CheckVariants { final class CheckN(private val n: Int) extends AnyVal { - def apply[R, A](rv: Gen[R, A])(test: A => TestResult): ZIO[R, Nothing, TestResult] = + def apply[R, A](rv: Gen[R, A])(test: A => TestResult): URIO[R, TestResult] = checkNM(n)(rv)(test andThen ZIO.succeedNow) - def apply[R, A, B](rv1: Gen[R, A], rv2: Gen[R, B])(test: (A, B) => TestResult): ZIO[R, Nothing, TestResult] = + def apply[R, A, B](rv1: Gen[R, A], rv2: Gen[R, B])(test: (A, B) => TestResult): URIO[R, TestResult] = checkN(n)(rv1 <*> rv2)(test.tupled) def apply[R, A, B, C](rv1: Gen[R, A], rv2: Gen[R, B], rv3: Gen[R, C])( test: (A, B, C) => TestResult - ): ZIO[R, Nothing, TestResult] = + ): URIO[R, TestResult] = checkN(n)(rv1 <*> rv2 <*> rv3)(reassociate(test)) def apply[R, A, B, C, D](rv1: Gen[R, A], rv2: Gen[R, B], rv3: Gen[R, C], rv4: Gen[R, D])( test: (A, B, C, D) => TestResult - ): ZIO[R, Nothing, TestResult] = + ): URIO[R, TestResult] = checkN(n)(rv1 <*> rv2 <*> rv3 <*> rv4)(reassociate(test)) def apply[R, A, B, C, D, F](rv1: Gen[R, A], rv2: Gen[R, B], rv3: Gen[R, C], rv4: Gen[R, D], rv5: Gen[R, F])( test: (A, B, C, D, F) => TestResult - ): ZIO[R, Nothing, TestResult] = + ): URIO[R, TestResult] = checkN(n)(rv1 <*> rv2 <*> rv3 <*> rv4 <*> rv5)(reassociate(test)) def apply[R, A, B, C, D, F, G]( rv1: Gen[R, A], @@ -591,7 +591,7 @@ package object test extends CompileVariants { rv6: Gen[R, G] )( test: (A, B, C, D, F, G) => TestResult - ): ZIO[R, Nothing, TestResult] = + ): URIO[R, TestResult] = checkN(n)(rv1 <*> rv2 <*> rv3 <*> rv4 <*> rv5 <*> rv6)(reassociate(test)) } diff --git a/website/sidebars.json b/website/sidebars.json index 01c05507bc05..e7969e6d1d0a 100644 --- a/website/sidebars.json +++ b/website/sidebars.json @@ -87,6 +87,7 @@ "about-sidebar": { "About": [ "about/about_index", + "about/about_coding_guidelines", "about/about_contributing", "about/about_coc" ]