Skip to content

Releases: zio/zio

2.0.1

18 Aug 15:40
659aa5f
Compare
Choose a tag to compare

This release contains miscellaneous bug fixes.

What's Changed

Read more

v1.0.16

16 Jul 00:25
dd114ff
Compare
Choose a tag to compare

What's Changed

New Contributors

Full Changelog: v1.0.15...v1.0.16

2.0.0

24 Jun 16:01
d6caead
Compare
Choose a tag to compare

The future of asynchronous and concurrent programming is finally here. We are excited to announce the release of ZIO 2.0!

Since we started on the journey to ZIO 2.0 we have focused on four key themes:

  • Performance - Frameworks such as ZIO are the base layer that higher level solutions are built on so they must be extremely efficient so you can build high performance applications on top of them.
  • Developer Experience - Just as important, you must be able to quickly and joyfully write clear, bug free code with ZIO.
  • Operations - ZIO must scale with you to support the needs of the most complex industry scale application with support for logging, metrics, and execution tracing.
  • Streams - Streaming is a fundamental paradigm for describing data flow pipelines and ZIO must provide support for it in a way that is both highly performant and deeply principled.

We believe we have achieved all of these goals to a greater extent than we could have hoped for when we started this journey.

These release notes will provide a high level overview of what we have done with regard to each of these points but when you are upgrading to ZIO 2.0 we would encourage you to check out the detailed migration guide available here. The migration guide also contains information about the automated migration tool you can use to do most of the heavy lifting for you.

Performance

ZIO 2.0 ships with the first ever "third generation" runtime that breaks new ground, avoiding use of the JVM stack whenever possible. This delivers dramatically higher performance than other runtimes for synchronous code, as shown in the following benchmark between ZIO 1.0, Cats Effect 3, and ZIO 2.0 that is typically used to compare runtimes.

[info] Benchmark        (depth)   Mode  Cnt     Score     Error  Units
[info] ZIO 1.x               20  thrpt   20   647.268 ±  12.892  ops/s
[info] Cats Effect 3.x       20  thrpt   20   947.935 ± 124.824  ops/s
[info] ZIO 2                 20  thrpt   20  1903.838 ±  21.224  ops/s

This runtime is highly optimized for Project Loom because in a post-Loom world there is never a need for asynchronous operations.

We will be continuing to optimize the performance of the new runtime for asynchronous operations and publish additional information regarding the new runtime. Until then, you can check out this blog post by John De Goes to learn more about the motivations and development process for the new runtime.

ZIO 2.0 also comes with a variety of other optimizations to improve performance of your application and prevent bottlenecks.

For example, appropriately managing blocking code is a typical "gotcha" for developers.

Runtimes such as ZIO typically perform all work on a small number of threads, typically equal to the number of operating system threads, to minimize contention and context switching. However, this means that if blocking work is inadvertently run on the core threadpool it can have a very negative impact on application performance, potentially resulting in deadlock.

ZIO 1.0 was the first runtime to provide tools to manage this through its Blocking service, which was later copied by other runtimes. However, this still required users to manually identify blocking work, which could be an error prone process, especially when legacy APIs did not clearly document blocking code.

ZIO 2.0 takes another giant leap forward here with the introduction of autoblocking. Now the ZIO runtime will automatically identify blocking work for you and safely shift it to a dedicated blocking thread pool. You can still use ZIO.blocking to provide a "hint" to the runtime that a certain section of code is blocking, but this is now merely an optimization. Early users have reported dramatic simplifications to their code by replacing all previous manual usages of blocking with this functionality.

Developer Experience

The speed of writing high quality code and onboarding developers can often be just as or more important than the speed of the code itself.

ZIO 1.0 broke new ground in developer productivity and teachability by taking a highly principled approach while eschewing unnecessary abstractions and jargon.

However, as we innovated in many areas in ZIO 1.0 such as the introduction of the environment type and support for dependency injection there were inevitably learning opportunities. We incorporated all that feedback and poured it back into ZIO 2.0 to make ZIO easier to use than ever before.

The area this is most visible is in the way you build your application’s required dependencies. This process is dramatically simplified in ZIO 2.0 with the deletion of the Has data type, automatic construction of layers, and a new more straightforward design pattern for defining layers.

trait MyService {
  def doSomething: ZIO[Any, MyDomainError, MyValue]
}

final case class MyServiceImplementation(dependency1: Dependency1, dependency2: Dependency2) extends MyService {
  def doSomething: ZIO[Any, MyDomainError, MyValue] =
    ???
}

val layer: ZLayer[Dependency1 & Dependency2, Nothing, MyService] =
  ZLayer {
    for {
      dependency1 <- ZIO.service[Dependency]
      dependency2 <- ZIO.service[Dependency2]
      ...         <- // do any necessary setup and finalization here
    } yield MyServiceImplementation(dependency1, dependency2)

In this way, layers leverage everything you already know about constructor based dependency injection while giving you the ability to perform ZIO workflows to setup and finalize your services in a principled way.

Even better, once you define these layers you can use automatic layer construction to assemble them together with extremely helpful compiler messages to help you as you go.

object MyApp extends ZIOAppDefault {

  val myAppLogic: ZIO[MyService, Nothing, Unit] =
    ???

  val run: ZIO[Any, Nothing, Unit] =
    myAppLogic.provide(layer)
    // compiler message guides you to provide layers for required dependencies
}

Another area where ZIO 2.0 is dramatically simplifying the user experience is around resources. ZIO 1.0 pioneered safe, powerful resource management with the ZManaged data type, which was the first to describe a resource as a value with powerful composition operators such as parallel acquisition of resources.

However, as great as ZManaged was, it was "one more thing to learn" and could create issues when users had to mix ZIO data types with ZManaged data types.

ZIO 2.0 makes this a thing of the past by deleting ZManaged and introducing the concept of a Scope, which is just something that we can add finalizers to and eventually close to run all those finalizers. A resource in ZIO 2.0 is now just represented as a scoped ZIO:

val resource: ZIO[R with Scope, E, A] = ???

This is a workflow that requires a Scope, indicating that part of this workflow requires finalization and needs a Scope to add that finalizer to. To remove the requirement for a Scope, the equivalent of ZManaged#use in ZIO 1.0, we simply use the ZIO.scoped operator:

ZIO.scoped {
  resource.flatMap(doSomethingWithIt)
}

In this way ZIO workflows that require finalization compose seamlessly with other ZIO workflows, because they all are just ZIO workflows. During the development process of ZIO 2.0 almost every ZIO ecosystem library has migrated to using scopes exclusively, typically resulting in dramatic simplifications to code.

We have also made significant efforts to simplify naming conventions, focusing on consistency of naming and accessibility.

For example, the constructor for creating a ZIO from a block of code that could throw exceptions was called effect in ZIO 1.0, which was really a holdover from a more Haskell / category theoretic perspective where throwing exceptions was a "side effect" versus a pure function (never mind that there are a variety of other "side effects" that do not consist of throwing exceptions).

In contrast, in ZIO 2.0 this constructor is simply called attempt. We "attempt" to do something which might fail, safely capturing the possibility of failure.

Similarly the constructor for creating a resource in ZIO 1.0 was bracket, which was another holdover from Haskell. While it has a certain metaphorical appeal (we are "bracketing" the use of a resource with acquire and release actions) it does not really tell us what is going on. In contrast in ZIO 2.0 this is called acquireRelease, saying precisely what it is.

Operations

While ZIO 2.0 makes it easier than ever to write correct code, it is inevitable in large applications that issues will arise, and you will need to be able to quickly diagnose and fix those issues.

ZIO 1.0 took significant steps to address these needs, including introducing asynchronous execution traces and providing support for logging and metrics through libraries such as ZIO Logging and ZIO Metrics.

However, there were some problems. While execution traces allowed you to see the trace for an asynchronous program in a way you never could before, traces could be hard to read and contained a lot of unnecessary information. Libraries such as ZIO Logging required you to add and integrate another dependency for what should be table stakes.

ZIO 2.0 is taking this to another level.

Execution traces now look just like JVM stack traces, letting you translate everything you know about reading stack traces into reading execution traces.

Logging is now build directly into ZIO so logging a line is as simple as:

val myAppLogic: ZIO[Any, Nothing, Unit] =
  ZIO.logInfo("Hello logging!")

Of course you can use operators like logLevel, logSpan, and logAnnotate just like you would with any other logging solution.
...

Read more

v1.0.15

03 Jun 23:48
22921ee
Compare
Choose a tag to compare

What's Changed

New Contributors

Full Changelog: v1.0.14...v1.0.15

2.0.0-RC6

02 May 23:45
a822917
Compare
Choose a tag to compare

Given the changes since RC5, we are doing one more release candidate for ZIO 2.0. This release candidate brings the following changes.

Autoblocking

Blocking work will now automatically be identified and shifted to the blocking executor. You can continue to use operators like blocking to give a "hint" to the runtime that a particular operator is blocking for efficiency but this should no longer be needed for correctness. This is a new feature and a significant innovation so please report any issues!

Simplification Of Runtime Customization

The RuntimeConfig and RuntimeConfigAspect data types have been deleted. Instead, you can customize the runtime using layers. This allows you to use ZIO workflows in setting up your runtime, for example to load some configuration information using ZIO Config.

If you need to access information about the current configuration you can use more specialized operators such as ZIO.logger or ZIO.executor.

If you want to customize the runtime you can use basic layers defined in the Runtime companion object such as addLogger or ZIO ecosystem libraries will provide their own layers that do all necessary setup. The layer in ZIOApp has been renamed bootstrap to more clearly indicate this.

Other Simplifications

The type alias "companion objects" have been deleted. So instead of Task.attempt(???) do ZIO.attempt(???). This promotes consistency and is one less thing to think about.

Variants of acquireRelease on the ZIO trait have been deleted in favor of the versions on the ZIO companion object. So instead of acquire.acquireReleaseWith(release)(use) do ZIO.acquireReleaseWith(acquire)(release)(use). This is more idiomatic as constructors belong on the companion object and promotes consistency between different ways of using resources.

The Accessible trait has been deleted. This proved a relatively unpopular alternative to implementing accessors compared to just using the macro annotation in Scala 2 or defining them manually, which has become easier with automatic completion tools. We are continuing to explore solutions for replacing macro annotations on Scala 3.

The Spec data type has been simplified to "build in" the TestFailure data type. So if you ever did layer.mapError(TestFailure.fail) in your tests before you can delete that.

The provideService operator has also been deleted. Instead of provideService(service) do provideLayer(ZLayer.succeed(service)). Layers are the idiomatic way of providing ZIO workflows with their dependencies and this operator generally should not be needed anymore with the elimination of the default services from the environment.

1.0.14

12 Apr 23:08
cb9a106
Compare
Choose a tag to compare

This release contains enhancements and bug fixes. It is binary compatible with prior ZIO 1.0 releases.

What's Changed

New Contributors

Full Changelog: v1.0.13...v1.0.14

2.0.0-RC5

08 Apr 22:41
113b24b
Compare
Choose a tag to compare

This release contains primarily bug fixes as we zero in on the final release of ZIO 2.0.

The only significant API changes are that the toLayer syntax has been replaced with ZLayer.fromFunction. Instead of (Users.apply _).toLayer you can do ZLayer.fromFunction(User.apply _). ZLayer.fromAcquireRelease(acquire)(release) has also been deleted and can be replaced with ZLayer.scoped(ZIO.acquireRelease(acquire)(release)). Similarly zio.toLayer can be replaced with ZLayer(zio).

With these changes, there are now just three ways to construct all layers:

  1. ZLayer.fromFunction for "simple" layers that depend on other services but do not require other ZIO workflows for their construction or finalization
  2. ZLayer.apply with a for comprehension for layers that require ZIO workflows for their construction but do not require finalization
  3. ZLayer.scoped with a for comprehension for layers that require finalization

This brings us to where we have always wanted to be with making defining the layers of your application as simple as possible. Please continue to report any bugs to us and we will be working diligently to prepare for the final release of ZIO 2.0. Thank you as always for your support.

2.0.0-RC4

01 Apr 23:17
3a35eef
Compare
Choose a tag to compare

We are excited to bring you what we hope will be the final release candidate of ZIO 2.0!

The theme of this release candidate is simplification. Throughout the development process we have striven to add features and performance while simplifying everything we possibly could, including the deletion of Has and ZManaged. This release builds on that in two ways.

First, the default ZIO services of Clock, Console, Random, and System have been removed from the environment and built into the ZIO runtime. These interfaces are very low level. Unlike other services, they describe functionality that users may want to use in any service or in their main application. And users rarely provide alternative implementations other than the test implementations provided by ZIO Test.

This change eliminates the boilerplate that was previously associated with manually providing these services in the implementation of higher level services. You can still modify the implementation of these services using ZEnv.services but we expect users will rarely need to do this themselves. All the operators you are used to like Console.printLine still work, they just won't add a requirement to the environment. And you can also use a new set of operators like ZIO.clock to access one of the default services directly.

As a user you should be able to just delete all references to these services from the environment.

Second, ZIO concurrent data structures such as Ref, Queue, and Hub have been simplified to delete their more polymorphic variants. While elegant, this functionality was rarely used and could be confusing for new users.

This release candidate also includes new support for streaming test results from ZIO Test based on work by @swoogles! Please let us know your feedback and we hope you enjoy the new more real time test output.

We continue to be on track to release the final version of ZIO 2.0 by the end of April. Between now and then will be continuing to optimize, fix bugs, and make minor API changes but we do not expect to make any further major changes at this point.

Thank you again for your support! Things just keep getting better and better!

2.0.0-RC3

18 Mar 17:05
7e5d7df
Compare
Choose a tag to compare

We are excited to bring you the third release candidate of ZIO 2.0!

This release candidate brings several exciting features. In particular it introduces scopes as first class values, which provide the basis for safe, composable resource handling.

We can construct a scoped resource using constructors such as ZIO.acquireRelease:

def file(name: String): ZIO[Scope, IOException, File] =
  ZIO.acquireRelease(openFile(name))(closeFile)

The Scope in the environment indicates that this workflow uses a scoped resource. We can work with the resource as much as we want and then when we are done with it we can close the scope with the ZIO.scoped operator:

ZIO.scoped {
  file(name).flatMap(useFile)
}

Any resources acquired within a Scope are guaranteed to be released when the Scope is closed, whether by success, failure, or interruption.

Scopes let us safely use resources while working with ordinary ZIO values without having to use any other data types like ZManaged. This means we have all the operators we are used to on ZIO and can work with any code that accepts a ZIO value.

Based on this work the ZManaged data type has been removed from ZIO but for backward compatibility it is still available as a separate zio-managed dependency.

This release also contains an overhauled metrics interface to make it easier for you to capture metrics about how your application is performing as well as a variety of other improvements.

Based on these changes we expect to do one more release candidate in approximately two weeks before pushing for a final version of ZIO 2.0.

Thank you as always for your support! We can't wait to all be able to use ZIO 2.0 together!

2.0.0-RC2

25 Jan 23:59
6639501
Compare
Choose a tag to compare

This release contains bug fixes and additional features to make it easier than ever to upgrade to ZIO 2, including new ZPipeline constructors that let you convert any transducer into a pipeline. This is also the first release in which ZIO Mock has been moved into its own project. Check out the new repository here if you are using it in your tests.

We expect that all major ZIO ecosystem libraries will be able to publish versions supporting RC2 shortly after it is released, allowing users to begin testing their applications on ZIO 2 if they are not already doing so. Thank you as always for your support and please continue to share your feedback as we drive towards the final release of ZIO 2!