or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

application.mdconcurrency.mdcore-effects.mddependency-injection.mderror-handling.mdindex.mdmetrics.mdresource-management.mdservices.mdstm.mdstreams.mdtesting.md
tile.json

services.mddocs/

Built-in Services

ZIO provides standard services for common application needs including console I/O, time operations, randomness, and system access, all following consistent patterns for testing and service substitution.

Capabilities

Console Service

Service for console input/output operations with proper error handling and encoding support.

/**
 * Service for console I/O operations
 */
trait Console {
  /** Print a value to stdout */
  def print(line: => Any)(implicit trace: Trace): IO[IOException, Unit]
  
  /** Print a line to stdout */
  def printLine(line: => Any)(implicit trace: Trace): IO[IOException, Unit]
  
  /** Print a value to stderr */
  def printError(line: => Any)(implicit trace: Trace): IO[IOException, Unit]
  
  /** Print a line to stderr */
  def printLineError(line: => Any)(implicit trace: Trace): IO[IOException, Unit]
  
  /** Read a line from stdin */
  def readLine(implicit trace: Trace): IO[IOException, String]
  
  /** Read a line from stdin with a prompt */
  def readLine(prompt: String)(implicit trace: Trace): IO[IOException, String]
}

// Static access methods
object Console {
  /** Print to stdout */
  def print(line: => Any): IO[IOException, Unit]
  
  /** Print line to stdout */
  def printLine(line: => Any): IO[IOException, Unit]
  
  /** Print to stderr */
  def printError(line: => Any): IO[IOException, Unit]
  
  /** Print line to stderr */
  def printLineError(line: => Any): IO[IOException, Unit]
  
  /** Read from stdin */
  def readLine: IO[IOException, String]
  
  /** Read from stdin with prompt */
  def readLine(prompt: String): IO[IOException, String]
}

Usage Examples:

import zio._

// Basic console I/O
val interactive = for {
  _    <- Console.printLine("What's your name?")
  name <- Console.readLine
  _    <- Console.printLine(s"Hello, $name!")
} yield ()

// Error handling with console
val safeConsole = for {
  result <- Console.readLine("Enter a number: ")
             .flatMap(input => ZIO.attempt(input.toInt))
             .catchAll(error => 
               Console.printLineError(s"Invalid input: $error") *>
               ZIO.succeed(-1)
             )
  _      <- Console.printLine(s"You entered: $result")
} yield ()

// Formatted output
val reportGeneration = for {
  users <- fetchUsers()
  _     <- Console.printLine("=== User Report ===")
  _     <- ZIO.foreach(users) { user =>
             Console.printLine(f"${user.id}%5d | ${user.name}%-20s | ${user.email}")
           }
  _     <- Console.printLine(s"Total users: ${users.length}")
} yield ()

// Interactive menu system
def showMenu(): IO[IOException, String] = {
  for {
    _ <- Console.printLine("\n=== Main Menu ===")
    _ <- Console.printLine("1. List users")
    _ <- Console.printLine("2. Add user")
    _ <- Console.printLine("3. Delete user")
    _ <- Console.printLine("4. Exit")
    choice <- Console.readLine("Select option: ")
  } yield choice
}

Clock Service

Service for time-related operations including current time, scheduling, and delays.

/**
 * Service for time-based operations
 */
trait Clock {
  /** Get current time in specified units */
  def currentTime(unit: => TimeUnit): UIO[Long]
  
  /** Get current time in specified chrono units */
  def currentTime(unit: => ChronoUnit): UIO[Long]
  
  /** Get current date and time */
  def currentDateTime: UIO[OffsetDateTime]
  
  /** Get current instant */
  def instant: UIO[Instant]
  
  /** Get current local date and time */
  def localDateTime: UIO[LocalDateTime]
  
  /** Get system nano time (for measuring durations) */
  def nanoTime: UIO[Long]
  
  /** Sleep for the specified duration */
  def sleep(duration: => Duration): UIO[Unit]
  
  /** Get the scheduler used by this clock */
  def scheduler: UIO[Scheduler]
  
  /** Get the underlying Java clock */
  def javaClock: UIO[java.time.Clock]
}

// Static access methods
object Clock {
  /** Get current time in units */
  def currentTime(unit: => TimeUnit): UIO[Long]
  
  /** Get system nano time */
  def nanoTime: UIO[Long]
  
  /** Sleep for duration */
  def sleep(duration: => Duration): UIO[Unit]
  
  /** Get current date/time */
  def currentDateTime: UIO[OffsetDateTime]
  
  /** Get current instant */
  def instant: UIO[Instant]
}

Usage Examples:

import zio._
import java.time._
import java.util.concurrent.TimeUnit

// Basic timing operations
val timingExample = for {
  start    <- Clock.nanoTime
  _        <- heavyComputation
  end      <- Clock.nanoTime
  duration  = (end - start) / 1_000_000 // Convert to milliseconds
  _        <- Console.printLine(s"Computation took ${duration}ms")
} yield ()

// Scheduled operations
val scheduler = for {
  _   <- Console.printLine("Starting periodic task...")
  _   <- (for {
           now <- Clock.currentDateTime
           _   <- Console.printLine(s"Heartbeat at $now")
         } yield ()).repeat(Schedule.fixed(10.seconds))
} yield ()

// Timeout with custom timing
val withTimeout = for {
  start  <- Clock.currentTime(TimeUnit.MILLISECONDS)
  result <- longRunningTask.timeout(30.seconds)
  end    <- Clock.currentTime(TimeUnit.MILLISECONDS)
  _      <- Console.printLine(s"Operation completed in ${end - start}ms")
} yield result

// Date/time formatting and processing
val dateProcessing = for {
  now       <- Clock.currentDateTime
  formatted  = now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
  _         <- Console.printLine(s"Current time: $formatted")
  
  // Check if it's business hours
  hour      <- Clock.localDateTime.map(_.getHour)
  isBusiness = hour >= 9 && hour < 17
  _         <- Console.printLine(s"Business hours: $isBusiness")
} yield ()

// Performance measurement
def measurePerformance[A](operation: UIO[A]): UIO[(A, Duration)] = {
  for {
    start  <- Clock.nanoTime
    result <- operation
    end    <- Clock.nanoTime
    duration = Duration.fromNanos(end - start)
  } yield (result, duration)
}

val performanceTest = for {
  (result, duration) <- measurePerformance(computeExpensiveValue())
  _                  <- Console.printLine(s"Result: $result, Time: $duration")
} yield ()

Random Service

Service for generating various types of random values with seedable and repeatable behavior.

/**
 * Service for random value generation
 */
trait Random {
  /** Generate random boolean */
  def nextBoolean: UIO[Boolean]
  
  /** Generate random int */
  def nextInt: UIO[Int]
  
  /** Generate random long */
  def nextLong: UIO[Long]
  
  /** Generate random float [0.0, 1.0) */
  def nextFloat: UIO[Float]
  
  /** Generate random double [0.0, 1.0) */
  def nextDouble: UIO[Double]
  
  /** Generate random gaussian (normal distribution) */
  def nextGaussian: UIO[Double]
  
  /** Generate random int in range [0, n) */
  def nextIntBounded(n: => Int): UIO[Int]
  
  /** Generate random int in range [min, max) */
  def nextIntBetween(min: => Int, max: => Int): UIO[Int]
  
  /** Generate random long in range [0, n) */
  def nextLongBounded(n: => Long): UIO[Long]
  
  /** Generate random long in range [min, max) */
  def nextLongBetween(min: => Long, max: => Long): UIO[Long]
  
  /** Generate random double in range [min, max) */
  def nextDoubleBetween(min: => Double, max: => Double): UIO[Double]
  
  /** Generate random float in range [min, max) */
  def nextFloatBetween(min: => Float, max: => Float): UIO[Float]
  
  /** Generate array of random bytes */
  def nextBytes(length: => Int): UIO[Chunk[Byte]]
  
  /** Generate random string of specified length */
  def nextString(length: => Int): UIO[String]
  
  /** Generate random printable character */
  def nextPrintableChar: UIO[Char]
  
  /** Generate random UUID */
  def nextUUID: UIO[UUID]
  
  /** Shuffle a collection randomly */
  def shuffle[A](collection: => Collection[A]): UIO[Collection[A]]
  
  /** Set the random seed */
  def setSeed(seed: => Long): UIO[Unit]
}

// Static access methods
object Random {
  def nextInt: UIO[Int]
  def nextBoolean: UIO[Boolean]
  def nextDouble: UIO[Double]
  def nextIntBounded(n: => Int): UIO[Int]
  def nextBytes(length: => Int): UIO[Chunk[Byte]]
  def nextUUID: UIO[UUID]
  def shuffle[A](list: => List[A]): UIO[List[A]]
}

Usage Examples:

import zio._
import java.util.UUID

// Basic random generation
val randomExample = for {
  randomInt    <- Random.nextInt
  randomDouble <- Random.nextDouble
  randomBool   <- Random.nextBoolean
  _            <- Console.printLine(s"Int: $randomInt, Double: $randomDouble, Bool: $randomBool")
} yield ()

// Game mechanics with random
val diceRoll = for {
  die1   <- Random.nextIntBounded(6).map(_ + 1)
  die2   <- Random.nextIntBounded(6).map(_ + 1)
  total   = die1 + die2
  _      <- Console.printLine(s"Rolled: $die1 + $die2 = $total")
} yield total

// Random selection and shuffling
val randomSelection = for {
  options  <- ZIO.succeed(List("Option A", "Option B", "Option C", "Option D"))
  shuffled <- Random.shuffle(options)
  selected <- shuffled.headOption match {
                case Some(choice) => ZIO.succeed(choice)
                case None => ZIO.fail("No options available")
              }
  _        <- Console.printLine(s"Selected: $selected")
} yield selected

// Generate test data
def generateTestUser(): UIO[TestUser] = {
  for {
    id       <- Random.nextUUID
    age      <- Random.nextIntBetween(18, 80)
    salary   <- Random.nextDoubleBetween(30000.0, 150000.0)
    active   <- Random.nextBoolean
    nameLen  <- Random.nextIntBetween(5, 15)
    name     <- Random.nextString(nameLen)
  } yield TestUser(id, name, age, salary, active)
}

// Simulation with seeded random
val reproducibleSimulation = for {
  _      <- Random.setSeed(12345)  // Set seed for reproducibility
  values <- ZIO.foreach(1 to 10)(_ => Random.nextGaussian)
  mean    = values.sum / values.length
  _      <- Console.printLine(s"Mean of gaussian values: $mean")
} yield mean

// Random delay for chaos engineering  
val chaosDelay = for {
  delayMs <- Random.nextIntBetween(100, 5000)
  _       <- Console.printLine(s"Adding chaos delay: ${delayMs}ms")
  _       <- Clock.sleep(delayMs.millis)
} yield ()

// Random sampling
def sampleFromList[A](list: List[A], sampleSize: Int): UIO[List[A]] = {
  for {
    shuffled <- Random.shuffle(list)
  } yield shuffled.take(sampleSize)
}

val samplingExample = for {
  allUsers    <- fetchAllUsers()
  sampleUsers <- sampleFromList(allUsers, 10)
  _           <- Console.printLine(s"Sampled ${sampleUsers.length} users for testing")
} yield sampleUsers

System Service

Service for accessing system environment variables, properties, and system information.

/**
 * Service for system environment and properties access
 */
trait System {
  /** Get environment variable */
  def env(variable: => String): IO[SecurityException, Option[String]]
  
  /** Get environment variable with fallback */
  def envOrElse(variable: => String, alt: => String): IO[SecurityException, String]
  
  /** Get environment variable with optional fallback */
  def envOrOption(variable: => String, alt: => Option[String]): IO[SecurityException, Option[String]]
  
  /** Get all environment variables */
  def envs: IO[SecurityException, Map[String, String]]
  
  /** Get system property */
  def property(prop: => String): IO[Throwable, Option[String]]
  
  /** Get system property with fallback */
  def propertyOrElse(prop: => String, alt: => String): IO[Throwable, String]
  
  /** Get system property with optional fallback */
  def propertyOrOption(prop: => String, alt: => Option[String]): IO[Throwable, Option[String]]
  
  /** Get all system properties */
  def properties: IO[Throwable, Map[String, String]]
  
  /** Get system line separator */
  def lineSeparator: UIO[String]
}

// Static access methods
object System {
  def env(variable: => String): IO[SecurityException, Option[String]]
  def envs: IO[SecurityException, Map[String, String]]
  def property(prop: => String): Task[Option[String]]
  def properties: Task[Map[String, String]]
  def lineSeparator: UIO[String]
  
  /** Current operating system */
  lazy val os: OS
  
  sealed trait OS {
    def isWindows: Boolean
    def isMac: Boolean  
    def isUnix: Boolean
    def isSolaris: Boolean
    def isUnknown: Boolean
  }
}

Usage Examples:

import zio._

// Configuration from environment
val loadConfig = for {
  port     <- System.env("SERVER_PORT").map(_.getOrElse("8080"))
  host     <- System.env("SERVER_HOST").map(_.getOrElse("localhost"))
  dbUrl    <- System.env("DATABASE_URL").someOrElse(
                ZIO.fail("DATABASE_URL environment variable is required")
              )
  debug    <- System.env("DEBUG").map(_.contains("true"))
  _        <- Console.printLine(s"Server config: $host:$port, DB: $dbUrl, Debug: $debug")
} yield AppConfig(host, port.toInt, dbUrl, debug)

// System information gathering
val systemInfo = for {
  javaVersion  <- System.property("java.version").map(_.getOrElse("unknown"))
  osName       <- System.property("os.name").map(_.getOrElse("unknown"))
  osVersion    <- System.property("os.version").map(_.getOrElse("unknown"))
  userHome     <- System.property("user.home").map(_.getOrElse("unknown"))
  tmpDir       <- System.property("java.io.tmpdir").map(_.getOrElse("/tmp"))
  lineSep      <- System.lineSeparator
  
  _            <- Console.printLine("=== System Information ===")
  _            <- Console.printLine(s"Java Version: $javaVersion")
  _            <- Console.printLine(s"OS: $osName $osVersion")
  _            <- Console.printLine(s"User Home: $userHome")
  _            <- Console.printLine(s"Temp Dir: $tmpDir")
  _            <- Console.printLine(s"Line Separator: ${lineSep.replace("\n", "\\n").replace("\r", "\\r")}")
} yield ()

// Platform-specific behavior
val platformSpecific = for {
  _        <- if (System.os.isWindows) {
                Console.printLine("Running Windows-specific code")
              } else if (System.os.isUnix) {
                Console.printLine("Running Unix-specific code")
              } else {
                Console.printLine("Running generic code")
              }
  pathSep  <- System.property("path.separator").map(_.getOrElse(":"))
  _        <- Console.printLine(s"Path separator: $pathSep")
} yield ()

// Environment variable validation
val validateEnvironment = for {
  requiredVars <- ZIO.succeed(List("DATABASE_URL", "API_KEY", "LOG_LEVEL"))
  
  missing <- ZIO.foldLeft(requiredVars)(List.empty[String]) { (acc, varName) =>
               System.env(varName).map {
                 case Some(_) => acc
                 case None => varName :: acc
               }
             }
             
  _       <- ZIO.when(missing.nonEmpty) {
               Console.printLineError(s"Missing required environment variables: ${missing.mkString(", ")}") *>
               ZIO.fail("Environment validation failed")
             }
             
  _       <- Console.printLine("Environment validation passed")
} yield ()

// Dynamic property loading
def loadPropertiesFromFile(filename: String): Task[Map[String, String]] = {
  for {
    userHome   <- System.property("user.home").someOrFail(new RuntimeException("user.home not set"))
    configPath  = s"$userHome/$filename"
    _          <- Console.printLine(s"Loading properties from: $configPath")
    // Implementation would read from file
    properties <- ZIO.succeed(Map("app.name" -> "MyApp", "app.version" -> "1.0"))
  } yield properties
}

// Environment-based feature flags
val featureFlags = for {
  allEnvVars <- System.envs
  flags       = allEnvVars.collect {
                  case (key, value) if key.startsWith("FEATURE_") => 
                    key.stripPrefix("FEATURE_") -> value.toBoolean
                }
  _          <- Console.printLine(s"Feature flags: $flags")
} yield flags

Service Testing and Mocking

Patterns for testing services and providing mock implementations.

// Mock console for testing
class TestConsole(
  inputs: Ref[List[String]],
  outputs: Ref[List[String]],
  errors: Ref[List[String]]
) extends Console {
  
  def print(line: => Any): IO[IOException, Unit] = 
    outputs.update(_ :+ line.toString).unit
    
  def printLine(line: => Any): IO[IOException, Unit] = 
    outputs.update(_ :+ (line.toString + "\n")).unit
    
  def printError(line: => Any): IO[IOException, Unit] = 
    errors.update(_ :+ line.toString).unit
    
  def printLineError(line: => Any): IO[IOException, Unit] = 
    errors.update(_ :+ (line.toString + "\n")).unit
    
  def readLine: IO[IOException, String] = 
    inputs.modify {
      case head :: tail => (head, tail)
      case Nil => throw new IOException("No more input available")
    }
    
  def readLine(prompt: String): IO[IOException, String] = 
    print(prompt) *> readLine
}

// Test clock for deterministic timing
class TestClock(timeRef: Ref[Long]) extends Clock {
  def currentTime(unit: TimeUnit): UIO[Long] = 
    timeRef.get.map(unit.convert(_, TimeUnit.MILLISECONDS))
    
  def nanoTime: UIO[Long] = 
    timeRef.get.map(_ * 1_000_000)
    
  def sleep(duration: Duration): UIO[Unit] = 
    timeRef.update(_ + duration.toMillis).unit
    
  def currentDateTime: UIO[OffsetDateTime] =
    timeRef.get.map(millis => 
      OffsetDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneOffset.UTC)
    )
    
  // ... other methods
}

// Deterministic random for testing
class TestRandom(seedRef: Ref[Long]) extends Random {
  def nextInt: UIO[Int] = 
    seedRef.updateAndGet(seed => (seed * 1103515245L + 12345L) & 0x7fffffffL).map(_.toInt)
    
  def nextBoolean: UIO[Boolean] = 
    nextInt.map(_ % 2 == 0)
    
  // ... other methods
}

Usage Examples:

// Service testing example
val testProgram = for {
  inputs  <- Ref.make(List("Alice", "30"))
  outputs <- Ref.make(List.empty[String])
  errors  <- Ref.make(List.empty[String])
  
  testConsole = new TestConsole(inputs, outputs, errors)
  
  // Run program with test console
  _ <- (for {
         name <- Console.readLine("Name: ")
         age  <- Console.readLine("Age: ")
         _    <- Console.printLine(s"Hello $name, you are $age years old")
       } yield ()).provideService(testConsole)
       
  // Verify outputs
  allOutputs <- outputs.get
  _          <- Console.printLine(s"Captured outputs: $allOutputs")
} yield ()