This part covers the ZIO tests. It is based on the ZIO document of Data Types. A small number of data types help you develop applications in a functional approach. All the data types are just values that
- describe effectful or asynchronous actions
- manage resources
- are used to make concurrent programming safe and simple.
1 Data Types for Side Effects
IO and newwork code interacte with outside thus they are impure, partial and having side effects. Most these calls can be encapsulated by the
UIO[A] data types.
- Pure values: All the
ZIOcan be used.
UIOrepresents code that can not fail.
- Effectful synchronous code:
- code can fail: the
- code cannot fail:
- handle failures:
refineOrDiekeeps some errors and fails with the rest.
- code can fail: the
- Effectful asynchronous code:
- code can fail:
- code cannot fail:
- code can fail:
All the effectful async method take a
register parameter that has a type of
(ZIO[R, E, A] => Unit) => Any. The first part is a callback function of type
ZIO[R, E, A] => Unit. The callback function will be used to wrap the async results that first converted into an effect.
The following code is an example of file processing. It may die with an exception because of the
A fiber is a lightweight thread of exeuction that are good for computation-intensive operation or async operations. Fibers are created by forking ZIO effects. Fibers can be joined or interrupted. Interruption terminates a fiber and safely releasing all resources.
2.1 Creating a Fiber
fork method of an effect generates an
URIO[R, Fiber[E, A]], An IO effect generates an
UIO[Fiber[E, A]]. A
Fiber[E, A] fails with an erorr of type
E or succeeds with a value of type
2.2 Terminating a Fiber
A fiber may be terminated for the following reasons:
- A fiber is slef-terminated or interrupted. The “main” fiber cannot be interrupted.
- A fiber fails to handle some error of type
E. This can happen only when an
IO.failis not handled.
- A fiber has a defect that leads to a non-recoverable error. There are two possible ways this can happen:
- A parital function (a function that may throw an exception) is passed to a high order function. Don’t pass inpure function to pure funciton.
- Error-throwing code was embedded into some value vai
Parallelism is achieved by using the
Par suffix methods such as
Two IO actions can
2.4 Scheduling and Shifting Thread
Fibers may shift thread. Fibers attempt to execute on the same thread for a configurable minimum period of time before yielding to other fibers. Fibers that resume from async callbacks will resume on the intiating thread and continue some time before yielding and resuming on the runtime thread pool. The configuration can be changed in
It’s the fiber’s counterpart for Java’s
ThreadLocal. Value is automatically propagated to child on fork and merged back in after joining child.
FiberRef.make[A] method will create an
3 Managed Resources
ZManaged[R, E, A] for a resource of type
A that require release after use. It automatically acquire resources before the use and release resources after use. The
ZManaged value wraps a value of type
ZIO[R, E, Reservation[R, E, A]] where the result has a type of
final case class Reservation[-R, +E, +A](acquire: ZIO[R, E, A], release: Exit[Any, Any] => ZIO[R, Nothing, Any]).
It comes with 5 related types:
The most used type is
Managed[E, A] that created by
Magaged object methods. For example:
A queue is created when
use is called and
shutdown will be call when
A managed can be created from an effect using
Managed.fromEffect or a pure value by
Managed.succeed, both don’t need a
ZManaged if the resource needs an environment of type
Managed can be combined using
Promise to communicate between two or more fibers.
Promise[E, A] is a variable of type
IO[E, A] that can be set excactly once. Creation of promise involves mutable memory allocation that should be described by an IO. Use
Promise.make to create a value of type
UIO[Promise[E, A]]. There are many ways to set its value:
succeed(a): succeed with a value of type
completeWith(effect): complete with an effect of type
die(new Throwable(...)): die with a value of
halt(e): fail with an error of type
All set methods returen a value of
UIO[Boolean] type that shows whether it is set (
true) or was already set(
await method to get a value from a promise. The awaiting fiber is suspended until the promise is set. Use
isDone method to check without blocking.
Queue[A] contatins values of type
A and has two basic operations:
take. Queue types include
The general type of
ZQueue[RA, RB, EA, EB, A, B] means that the queue can be offered
A, depends on
RA and may fail with
EA. It can yield value of type
B, depends on
RB and may fail with
Queue[A] is defined as
type Queue[A] = ZQueue[Any, Nothing, Any, Nothing, A, A] that means it cannot fail, doesn’t depend on any resource and provide the same type of value as its offered value type.
Queue operations include
ZRef type is used for concurrently read and write a single immutable-value. The
Ref object exposes two methods of
ZRef[EA, EB, A, B] has two fundamental operations:
set sets a value of type
A and may fail with an error of type
get gets a value of
B and may fail with an error of type
EB. When the error and value types of the
ZRef are unified as
ZRef[E, E, A, A], the
ZRef also supports atomic
update operations. All operations are guaranteed to be safe for concurrent access. The value of
ZRef should be immutable to keep this gurantee because it is implemented using campare and swap operatons in a loop rather than synchronization for performance reason.
Semaphore data type synchronize fibers by safely acquiring and releasing a permit. It is based on
Semaphore.make(permits): to create a semaphore of type
UIO[Semaphore]with the specified number of permits.
available: the nubmer of available permits
withPermit(task): acquire a permit for a task
When task is completed, whether succeeds or fails, the acquired permits are released.
Schedules are composable recurrence used for repetition and retries.
Schedule[R, A, B] consumes
A values, and based on the inputs and the internal state, decides whether to continue or halt. Every decsioin has a (possibly zero) delay, and an output value of type
B. Schedules can be composed by
&& (use the longer one),
|| (use the shorter one),
andThen(run in sequence, one after another one).
Chunk[A] represents a chunk of values of type
A. It is backed by arrays but expose a purely functional, safe interface and is lazy on operations.
10 STM Data Types