ZIO v1.0.0-RC18 Release Notes
Release Date: 2020-03-03 // about 4 years ago-
๐ ZIO 1.0.0-RC18 is the last expected release candidate for ZIO 1.0. There are no breaking changes between RC18 and 1.0, but no guarantees. The current plan is to release 1.0 within a few weeks after RC18, to ensure production-worthiness of 1.0.
Note: All deprecated methods will be deleted for ZIO 1.0.
ZIO Environment
Previously, the official recommended way to use ZIO Environment was to assemble pieces using inheritance to create larger environments out of smaller environments.
In the absence of proxies, this led to some pain around construction of the environment, because creating one class from other classes is not done using value-based operators, but inheritance-specific language features that do not permit abstraction.
Moreover, this approach had several drawbacks:
- Providing just part of an environment was difficult, and was not possible to do generically (the remaining part of the environment had to be known statically).
- โก๏ธ It was not possible to dynamically update parts of the environment inside scoped regions, which led to pain when trying to customize services in parts of the application.
Finally, the principal way to make a first service depend on a second service was to list a reference to the second service inside the first (recalling the Cake Pattern self-type). This led to a pattern whereby business logic would express its dependencies by using ZIO Environment, but all other parts of the application would express their dependencies using fields.
These problems have been solved in RC18 with the introduction of two new data types: Has and ZLayer. Together they have led to a new pattern of encoding environmental dependencies, which has been rolled out in ZIO Core services (Clock, Console, Random, System).
While it is still technically possible to use the older pattern, the official ZIO ecosystem (all projects in the ZIO organization on Github) will be migrated to the new pattern.
Has
The new recommended way to assemble environments involves using a new structure called Has. The Has data type has operators for building bigger environments out of smaller environments, including add, which adds a service to a Has, and concat, which merges two Has into one.
ZLayer
ZLayer is a value that represents a recipe for constructing some services in terms of other services. It is similar to a constructor in a Java or Scala application, which takes the services it depends on, and returns the service it constructs (constructor-based dependency injection). Unlike constructors, however, ZLayers are first-class values that compose type-safely in several ways, and they can construct many services, not just one. Additionally, they can describe the effectful and resourceful construction, such as connecting to a database, creating a database thread pool, and when the service is no longer required, freeing the thread pool, and disconnecting from the database.
๐ ZLayer represents the most power you could ever want in describing the construction of a service. In the end, almost all applications need something with this power (as witnessed by the fact that, generally, users of previous ZIO versions resorted to building the environment using ZManaged).
๐ ZLayers can be constructed using a variety of constructors in the companion object of ZLayer. They can be composed horizontally (when one service depends on another), and vertically (for two independent services). Services that are repeated in the graph are automatically shared, to avoid duplicate construction and release. Further, services that can be constructed in parallel are automatically constructed in parallel, to optimize service construction time.
The general pattern of ZIO Environment is now as follows:
type UserRepo = Has[UserRepo.Service]object UserRepo { trait Service { def getUserById(id: Id): Task[User] } def getUserById(id: Id): RIO[UserRepo, User] = ZIO.accessM(\_.get.getUserById(id)) }
Then typically, a
live
value is placed in the companion object of the service, which uses ZLayer to construct a production version of the service in terms of its dependencies:val live: ZLayer[Database, Nothing, UserRepo] = ???
Services may be provided to effects that need them using the ZIO#provideLayer method, e.g.
myEffect.provideLayer(UserRepo.live)
.ZIO Runtime System
Weak automatic supervision
Supervision is now backed by weak sets, which means that child fibers may be freely garbage collected when they cannot resume. This should address memory leaks in scenarios where large numbers of non-terminating fibers are created.
Structured concurrency
0๏ธโฃ All child fibers are automatically bound to their parent fiber. When the parent fiber exits, the child fiber will be interrupted or disowned, as determined by the
SuperviseMode
it is forked with. The default supervision mode for forked fibers is interruption, which means that by default, when a parent fiber exits, its child fibers will be interrupted. This ensures fibers do not leak.๐ In order to recapture the previous behavior, one may use
forkDaemon
, ordisown
, which explicitly disowns a child fiber and moves it to a root set of fibers.Safer race
Race no longer forces either side to be interruptible. Users may have to perform
left.interruptible.race(right.interruptible)
to acquire the old behavior (which punched holes in uninterruptible regions); or possibly,left.disconnect.race(right.disconnect)
(if they want the effects to be keep running when interrupted, but in the background).๐ New combinators
Several new combinators are added:
๐disconnect
โ disconnects interruption of the effect from its parent. This allows "early interruption" without waiting for finalization. It replaces the need for allxyzFork
variants, which are now deprecated. It's quite useful withrace
, with or withoutinterruptible
.
disown
โ Called by a parent fiber to disown a child fiber. Relocates the child to the root set.๐ New ZIO.never
โ When
ZIO.never
is forked, it will be garbage collected, soZIO.never.onInterrupt(putStrLn("Bye"))
will never execute the finalizer. This can be inconvenient for testing, soZIO.infinity
is added which is likenever
, but won't be garbage collected.Deletion of Daemon Mode
Daemon mode has been deleted because in practice, code never knows whether grandchildren should be daemons, only whether immediate children should be daemons; and generally, daemon mode is a one-off for a specific forked fiber.
In place of daemon mode,
effect.forkDaemon
can be used, which is the composition of two other operators: ordinaryfork
, followed by an immediatedisown
of the child fiber.๐ Deprecation of *Fork Variations
๐ All
xyzFork
variants are deprecated or removed. For example,bracketFork
. This is thanks to the newdisconnect
operator which allows for a much more compositional approach to solving this problem. Now if you don't want interruption of something to wait around for completion, just useeffect.disconnect
.๐ Performance enhancements for blocking effects
๐ Blocking effects has seen several enhancement regarding performance, with potential change in semantic:
- ๐ Now, calling
lock
on a fiber first checks to see if the fiber is already on the correct Executor and does nothing if in that case; - โช
effectBlocking
doesnโt interrupt the underlying thread anymore in case of fiber interruption. If you want to revert to previous behavior, useeffectBlockingInterrupt
. - โก๏ธ
Blocking
executor ergonomics were updated to enhance thread reuse and limit risk to crash the server in case of massive thread leak.
ZIO General API
Laziness
Effect constructors are now lazy. This is done to ensure that if users embed side-effects in unexpected places, then errors are managed by ZIO.
๐ Deprecated traverse/sequence
๐ Rather than have duplicate names for operators, the decision was made to deprecate the "classic names" for traverse/sequence, in favor of friendlier and more descriptive names foreach/collectAll.
โ ZIO Test
Inheritance Based Specs
0๏ธโฃ Spes are now defined by inheriting from
DefaultRunnableSpec
instead of using constructor arguments:object ExampleSpec extends DefaultRunnableSpec { def spec = suite(โExampleSpec)( test(โaddition worksโ) { assert(1 + 1)(equalTo(2)) } ) }
โ This provides a more natural syntax for writing tests and allows defining data or helper methods in the same object instead of requiring a separate utility object.
Type Safe Equality
โ To provide increased type safety, the syntax for assertions has changed from
assert(1 + 1, equalTo(2))
toassert(1 + 1)(equalTo(2))
. This curried syntax allows the test framework to issue a compilation error if an equality assertion is comparing two unrelated types, catching bugs earlier. A ScalaFix migration rule is provided to automatically rewrite assertions to the new curried syntax.๐ Improved Test Console Behavior
๐ง To facilitate debugging, by default the
TestConsole
will now render output to the standard output in addition to writing it to the output buffer. This feature is configurable on a scoped basis using thedebug
andsilent
methods onTestConsole
or the corresponding test aspects.โ Test Annotations
๐ Test annotations provide flexible information for reporting metadata about tests. ZIO Test ships with a variety of test annotations that will automatically track metrics such as the number of ignored tests, the number of times a test was retried using
flaky
and the number of times a test was repeated usingnonFlaky
. Users can apply additional annotations using test aspects, for example timing the execution of tests and showing the slowest tests usingtimed
or labeling tests withtag
.โ In the future test annotations will be the basis for additional structured test reporting to support rich analysis of test results both across suites and over time.
๐คก Spy Mocking Functionality
๐ The
mock
package contains new functionality for spy mocks in theSpyable
trait. Spies have the ability to wrap an existing service and capture all method calls to the service being spied on as well as inputs and outputs, making it easy to verify the behavior of elements of complex systems. See the Scaladoc for additional information on this new feature.Providing The Environment
โ The functionality provided by layers discussed above makes it much for users to provide tests with additional environmental requirements. In particular, the
provideCustomLayer
andprovideCustomLayerShared
methods make it extremely easy to add an additional dependency to theTestEnvironment
, which previously required significant boilerplate.๐ New Aspects
โ A variety of new test aspects have been added to provide more flexibility than ever in modifying test execution:
- โ
debug
โ RendersTestConsole
output to standard output for the scope of a test. - โ
diagnose
โ Runs a test on its own fiber and performs a fiber dump of the test fiber and all of its children if the test does not complete within the specified time. This can be extremely useful for diagnosing concurrency bugs. - โ
forked
โ Runs each test on its own fiber. - โ
noDelay
โ Automatically runs all effects involving theTestClock
without it needing to be manually adjusted. - โ
nonTermination
โ Tests that an effect does not terminate within a specified time. - ๐
nondeterministic
โ Runs each test with a random seed generated from the system time. - ๐
setSeed
โ Sets the random seed for a test. This is useful to deterministically "replay" tests. - โ
silent
โ PreventsTestConsole
output from being rendered to standard output for the scope of a test. - โ
tag
โ labels tests with the specified tag. Tests can be filtered based on tags and tags will be displayed along with test output. - โ
verify
โ Verifies that a postcondition holds after each test.
โ Additional Generator Combinators
A variety of additional combinators have been provided for composing generators. In particular, the
zip
variants allow composing multiple generators in parallel when the generators do not depend on each other. This allows for more efficient shrinking and can be particularly good for large case classes or similar data structure. Additional generators are also provided for types such asjava.util.UUID
.JUnit integration *
๐ A custom JUnit runner is provided for running ZIO Test specs under other build tools (like Maven, Gradle, Bazel, etc.) and under IDEs.
๐ To get the runner, add the equivalent of following dependency definition under your build tool:
libraryDependencies ++= Seq("dev.zio" %% "zio-test-junit"% zioVersion % "test")
๐ To make your spec appear as a JUnit test to build tools and IDEs, convert it to a class and annotate it with
@RunWith(classOf[zio.test.junit.ZTestJUnitRunner])
or simply extendzio.test.junit.JUnitRunnableSpec
.Automatic Derivation of Generators Powered By Magnolia
Automatic derivation of generators for case classes and sealed traits is now available powered by Magnolia. To get it, add the following to your dependencies:
libraryDependencies ++= Seq( โdev.zioโ %% โzio-test-magnoliaโ %% zioVersion % โtest )
You can then automatically derive generators for your own data types:
final case class Person(name: String, age: Int)val genPerson: Gen[Random with Sized, Person] = DeriveGen[Person]sealed trait Colorcase object Redextends Colorcase object Green extends Colorcase object Blueextends Colorval genColor: Gen[Random with Sized, Color] = DeriveGen[Color]
Experimental Polymorphic Generators
๐ ZIO Test now includes experimental support for polymorphic generators. These generators do not require the user to specify a particular type but instead generate random data from a variety of types that satisfy specified constraints, such as having an
Ordering
. This can be useful for testing highly polymorphic code in a generic way. See the examples in thezio.test.poly
package and please provide feedback on this feature.ZIO Streams
ZIO Streams includes a number of new combinators, which can be explored through the Scaladoc, but no significant breaking changes were introduced.
ZIO STM
ZIO STM has undergone a large amount of development work, adding more power, more safety, and more structures.
More Methods
STM now features many more methods, which mirror those on ZIO structures; and STM structures like TArray now have many more useful methods on them.
Stack Safety
๐ STM is now stack safe and can handle transactions of any size without stack overflows. This is achieved not with trampolining but by using exceptions for control-flow. There is a performance hit but it is minimal for the added stack safety.
Environment
STM itself has been generalized to ZSTM, permitting use of ZIO Environment. This is useful to embed STM structures directly into the environment, which allows transactions to operate against application-wide data structures, such as caches, counters, and the like.
๐ New Structures
TMap
TQueue
TReentrantLock
Contributors
There were a total of 67 contributors to RC18.
Thank you to Adam Fraser, Ajay Chandran, Alex Savin, Alexander van Olst, Bojan Babiฤ, Bojan Blagojeviฤ, Boris V.Kuznetsov, Chris Andre, dariusrobson, Dejan Mijiฤ, Dmitry Karlinsky, Dragutin Marjanoviฤ, Evgeny Veretennikov, Fabio Serragnoli, felher, Ferdinand Svehla, Greg Holland, Ievgen Garkusha, Igal Tabachnik, ioleo, Isaias Bartelborth, Itamar Ravid, Jan Toebes, Jens Hoffmann, John A. De Goes, Jonathan Winandy, Kai, Koleman Nix, Laurynas Lubys, Luis Miguel Mejรญa Suรกrez, Marek Kadek, Mateusz Sokรณลโ, Matthias Langer, Maxim Davydov, Maxim Schuwalow, montrivo, Nadav Samet, Oleksandra Holubitska, Oliver Wickham, Pascal Mengelt, Pavel Shirshov, Pavels Sisojevs, Paweล Kiersznowski, peterlopen, Philippe Derome, Pierangelo Cecchetto, Pierre Ricadat, Rafael Saraiva Figueiredo, Regis Kuckaertz, reibitto, Richard Whaling, Roberto Leibman, Salar Rahmanian, Sergey Rublev, simpadjo, sken, svroonland, TapanVaishnav, Tibor Erdesz, Unclebob, Vasil Vasilev, Vasiliy Levykin, Vitalii, Wiem Zine El Abidine, wongelz, and Zachary Albia for your contributions to ZIO.
It is our incredible community of users and contributors that make ZIO possible.