Want to learn about Standard ZIO Services?

Reading Time: 3 minutes

In this blog, we’ll talk about Standard ZIO Services and why it’s useful for us as programmers and how we can use them.Services provide well-defined interfaces that can be implemented differently in testing environments and production environments.

zio

Build-in services

When we use the services we don’t need to provide their corresponding environment explicitly. ZIO provides built-in live version of ZIO services to our effects, so we do not need to provide them manually.

ZIO already provides five build-in services:

  • Clock : Contains some functionality related to time and scheduling.
  • Random : Provides utilities to generate random numbers.
  • Console : Operations for reading/writing strings from/to the standard input, output, and error console.
  • System : Contains several useful functions related to system environments and properties.
  • Blocking : Provides functionality for running blocking tasks on a separate Executor optimised for these kinds of workloads.

Clock

The Clock service has a very useful functionality for sleeping and creating a delay between jobs. The sleep takes a Duration and sleeps for the specified duration.

It is similar to java.lang.Thread.sleep function, but it doesn’t block any underlying thread. It’s completely non-blocking.

def delay: ZIO[Any, Throwable, Nothing] =
  Clock.currentDateTime.flatMap(Console.printLine(_)) *>
    ZIO.sleep(1.seconds) *> delay

The Clock service also provides functionality related to time and scheduling. This includes several methods to obtain the current time in different ways as currentTime to return the current time in the specified TimeUnit, currentDateTime to return the current OffsetDateTime, and nanoTime to obtain the current time in nanoseconds.

val inMilli: UIO[Long] = Clock.currentTime(TimeUnit.MILLISECONDS)
val inDays:        UIO[Long] = Clock.currentTime(TimeUnit.DAYS)

Random

Random service provides utilities to generate random numbers. It’s a functional wrapper of scala.util.Random. This service contains various different pseudo-random generators like nextIntnextBoolean and nextDouble. Each random number generator functions return a URIO[Random, T] value.

val int: ZIO[Any, Nothing, Int] =
ZIO.effectTotal(scala.util.Random.nextInt())

The Random service is sometimes used in generic code in scheduling, such as when adding a random delay between recurrences of some effect.

Console

The Console service contains simple I/O operations for reading/writing strings from/to the standard input, output, and error console.

All functions of the Console service are effectful, this means they are just descriptions of reading/writing from/to the console.

package object console {
val getStrLn: ZIO[Console, IOException, String]
def putStr(line: => String): URIO[Console, Unit]
def putStrLn(line: => String): URIO[Console, Unit]

The key methods on the Console service are getStrLn, which is analogous to
readLine() and putStrLn, which is the equivalent of println. There is also a
putStr method if you do not want to add a newline after printing text to the
console.

System

The System service provides functionality to get system and environment variables. System service contains several useful functions related to system environments and properties. Both of system environments and system properties are key/value pairs. Also used to pass user-defined information to our application.

package object system {
def env(variable: String): IO[SecurityException, Option[String]]
def property(prop: String): IO[Throwable, Option[String]]

Environment variables are global operating system level variables available to all applications running on the same machine, while properties are application-level variables provided to our application.

Blocking

The Blocking service supports running blocking effects on an Executor optimized for blocking tasks. By default, the ZIO runtime is optimized for asynchronous and computationally-bound tasks, with a small fixed number of threads that perform all work.

import zio.blocking._
def blocking(n: Int) = effectBlocking {
  do {
    println(s"Running blocking task number $n on dedicated blocking thread pool")
    Thread.sleep(3000) 
  } while (true)
}

The most fundamental is the blocking operator, which have an effect and ensures it will be run on the blocking thread pool.

Discover more from Knoldus Blogs

Subscribe now to keep reading and get access to the full archive.

Continue reading