or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

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

services.mddocs/

0

# Built-in Services

1

2

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.

3

4

## Capabilities

5

6

### Console Service

7

8

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

9

10

```scala { .api }

11

/**

12

* Service for console I/O operations

13

*/

14

trait Console {

15

/** Print a value to stdout */

16

def print(line: => Any)(implicit trace: Trace): IO[IOException, Unit]

17

18

/** Print a line to stdout */

19

def printLine(line: => Any)(implicit trace: Trace): IO[IOException, Unit]

20

21

/** Print a value to stderr */

22

def printError(line: => Any)(implicit trace: Trace): IO[IOException, Unit]

23

24

/** Print a line to stderr */

25

def printLineError(line: => Any)(implicit trace: Trace): IO[IOException, Unit]

26

27

/** Read a line from stdin */

28

def readLine(implicit trace: Trace): IO[IOException, String]

29

30

/** Read a line from stdin with a prompt */

31

def readLine(prompt: String)(implicit trace: Trace): IO[IOException, String]

32

}

33

34

// Static access methods

35

object Console {

36

/** Print to stdout */

37

def print(line: => Any): IO[IOException, Unit]

38

39

/** Print line to stdout */

40

def printLine(line: => Any): IO[IOException, Unit]

41

42

/** Print to stderr */

43

def printError(line: => Any): IO[IOException, Unit]

44

45

/** Print line to stderr */

46

def printLineError(line: => Any): IO[IOException, Unit]

47

48

/** Read from stdin */

49

def readLine: IO[IOException, String]

50

51

/** Read from stdin with prompt */

52

def readLine(prompt: String): IO[IOException, String]

53

}

54

```

55

56

**Usage Examples:**

57

58

```scala

59

import zio._

60

61

// Basic console I/O

62

val interactive = for {

63

_ <- Console.printLine("What's your name?")

64

name <- Console.readLine

65

_ <- Console.printLine(s"Hello, $name!")

66

} yield ()

67

68

// Error handling with console

69

val safeConsole = for {

70

result <- Console.readLine("Enter a number: ")

71

.flatMap(input => ZIO.attempt(input.toInt))

72

.catchAll(error =>

73

Console.printLineError(s"Invalid input: $error") *>

74

ZIO.succeed(-1)

75

)

76

_ <- Console.printLine(s"You entered: $result")

77

} yield ()

78

79

// Formatted output

80

val reportGeneration = for {

81

users <- fetchUsers()

82

_ <- Console.printLine("=== User Report ===")

83

_ <- ZIO.foreach(users) { user =>

84

Console.printLine(f"${user.id}%5d | ${user.name}%-20s | ${user.email}")

85

}

86

_ <- Console.printLine(s"Total users: ${users.length}")

87

} yield ()

88

89

// Interactive menu system

90

def showMenu(): IO[IOException, String] = {

91

for {

92

_ <- Console.printLine("\n=== Main Menu ===")

93

_ <- Console.printLine("1. List users")

94

_ <- Console.printLine("2. Add user")

95

_ <- Console.printLine("3. Delete user")

96

_ <- Console.printLine("4. Exit")

97

choice <- Console.readLine("Select option: ")

98

} yield choice

99

}

100

```

101

102

### Clock Service

103

104

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

105

106

```scala { .api }

107

/**

108

* Service for time-based operations

109

*/

110

trait Clock {

111

/** Get current time in specified units */

112

def currentTime(unit: => TimeUnit): UIO[Long]

113

114

/** Get current time in specified chrono units */

115

def currentTime(unit: => ChronoUnit): UIO[Long]

116

117

/** Get current date and time */

118

def currentDateTime: UIO[OffsetDateTime]

119

120

/** Get current instant */

121

def instant: UIO[Instant]

122

123

/** Get current local date and time */

124

def localDateTime: UIO[LocalDateTime]

125

126

/** Get system nano time (for measuring durations) */

127

def nanoTime: UIO[Long]

128

129

/** Sleep for the specified duration */

130

def sleep(duration: => Duration): UIO[Unit]

131

132

/** Get the scheduler used by this clock */

133

def scheduler: UIO[Scheduler]

134

135

/** Get the underlying Java clock */

136

def javaClock: UIO[java.time.Clock]

137

}

138

139

// Static access methods

140

object Clock {

141

/** Get current time in units */

142

def currentTime(unit: => TimeUnit): UIO[Long]

143

144

/** Get system nano time */

145

def nanoTime: UIO[Long]

146

147

/** Sleep for duration */

148

def sleep(duration: => Duration): UIO[Unit]

149

150

/** Get current date/time */

151

def currentDateTime: UIO[OffsetDateTime]

152

153

/** Get current instant */

154

def instant: UIO[Instant]

155

}

156

```

157

158

**Usage Examples:**

159

160

```scala

161

import zio._

162

import java.time._

163

import java.util.concurrent.TimeUnit

164

165

// Basic timing operations

166

val timingExample = for {

167

start <- Clock.nanoTime

168

_ <- heavyComputation

169

end <- Clock.nanoTime

170

duration = (end - start) / 1_000_000 // Convert to milliseconds

171

_ <- Console.printLine(s"Computation took ${duration}ms")

172

} yield ()

173

174

// Scheduled operations

175

val scheduler = for {

176

_ <- Console.printLine("Starting periodic task...")

177

_ <- (for {

178

now <- Clock.currentDateTime

179

_ <- Console.printLine(s"Heartbeat at $now")

180

} yield ()).repeat(Schedule.fixed(10.seconds))

181

} yield ()

182

183

// Timeout with custom timing

184

val withTimeout = for {

185

start <- Clock.currentTime(TimeUnit.MILLISECONDS)

186

result <- longRunningTask.timeout(30.seconds)

187

end <- Clock.currentTime(TimeUnit.MILLISECONDS)

188

_ <- Console.printLine(s"Operation completed in ${end - start}ms")

189

} yield result

190

191

// Date/time formatting and processing

192

val dateProcessing = for {

193

now <- Clock.currentDateTime

194

formatted = now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)

195

_ <- Console.printLine(s"Current time: $formatted")

196

197

// Check if it's business hours

198

hour <- Clock.localDateTime.map(_.getHour)

199

isBusiness = hour >= 9 && hour < 17

200

_ <- Console.printLine(s"Business hours: $isBusiness")

201

} yield ()

202

203

// Performance measurement

204

def measurePerformance[A](operation: UIO[A]): UIO[(A, Duration)] = {

205

for {

206

start <- Clock.nanoTime

207

result <- operation

208

end <- Clock.nanoTime

209

duration = Duration.fromNanos(end - start)

210

} yield (result, duration)

211

}

212

213

val performanceTest = for {

214

(result, duration) <- measurePerformance(computeExpensiveValue())

215

_ <- Console.printLine(s"Result: $result, Time: $duration")

216

} yield ()

217

```

218

219

### Random Service

220

221

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

222

223

```scala { .api }

224

/**

225

* Service for random value generation

226

*/

227

trait Random {

228

/** Generate random boolean */

229

def nextBoolean: UIO[Boolean]

230

231

/** Generate random int */

232

def nextInt: UIO[Int]

233

234

/** Generate random long */

235

def nextLong: UIO[Long]

236

237

/** Generate random float [0.0, 1.0) */

238

def nextFloat: UIO[Float]

239

240

/** Generate random double [0.0, 1.0) */

241

def nextDouble: UIO[Double]

242

243

/** Generate random gaussian (normal distribution) */

244

def nextGaussian: UIO[Double]

245

246

/** Generate random int in range [0, n) */

247

def nextIntBounded(n: => Int): UIO[Int]

248

249

/** Generate random int in range [min, max) */

250

def nextIntBetween(min: => Int, max: => Int): UIO[Int]

251

252

/** Generate random long in range [0, n) */

253

def nextLongBounded(n: => Long): UIO[Long]

254

255

/** Generate random long in range [min, max) */

256

def nextLongBetween(min: => Long, max: => Long): UIO[Long]

257

258

/** Generate random double in range [min, max) */

259

def nextDoubleBetween(min: => Double, max: => Double): UIO[Double]

260

261

/** Generate random float in range [min, max) */

262

def nextFloatBetween(min: => Float, max: => Float): UIO[Float]

263

264

/** Generate array of random bytes */

265

def nextBytes(length: => Int): UIO[Chunk[Byte]]

266

267

/** Generate random string of specified length */

268

def nextString(length: => Int): UIO[String]

269

270

/** Generate random printable character */

271

def nextPrintableChar: UIO[Char]

272

273

/** Generate random UUID */

274

def nextUUID: UIO[UUID]

275

276

/** Shuffle a collection randomly */

277

def shuffle[A](collection: => Collection[A]): UIO[Collection[A]]

278

279

/** Set the random seed */

280

def setSeed(seed: => Long): UIO[Unit]

281

}

282

283

// Static access methods

284

object Random {

285

def nextInt: UIO[Int]

286

def nextBoolean: UIO[Boolean]

287

def nextDouble: UIO[Double]

288

def nextIntBounded(n: => Int): UIO[Int]

289

def nextBytes(length: => Int): UIO[Chunk[Byte]]

290

def nextUUID: UIO[UUID]

291

def shuffle[A](list: => List[A]): UIO[List[A]]

292

}

293

```

294

295

**Usage Examples:**

296

297

```scala

298

import zio._

299

import java.util.UUID

300

301

// Basic random generation

302

val randomExample = for {

303

randomInt <- Random.nextInt

304

randomDouble <- Random.nextDouble

305

randomBool <- Random.nextBoolean

306

_ <- Console.printLine(s"Int: $randomInt, Double: $randomDouble, Bool: $randomBool")

307

} yield ()

308

309

// Game mechanics with random

310

val diceRoll = for {

311

die1 <- Random.nextIntBounded(6).map(_ + 1)

312

die2 <- Random.nextIntBounded(6).map(_ + 1)

313

total = die1 + die2

314

_ <- Console.printLine(s"Rolled: $die1 + $die2 = $total")

315

} yield total

316

317

// Random selection and shuffling

318

val randomSelection = for {

319

options <- ZIO.succeed(List("Option A", "Option B", "Option C", "Option D"))

320

shuffled <- Random.shuffle(options)

321

selected <- shuffled.headOption match {

322

case Some(choice) => ZIO.succeed(choice)

323

case None => ZIO.fail("No options available")

324

}

325

_ <- Console.printLine(s"Selected: $selected")

326

} yield selected

327

328

// Generate test data

329

def generateTestUser(): UIO[TestUser] = {

330

for {

331

id <- Random.nextUUID

332

age <- Random.nextIntBetween(18, 80)

333

salary <- Random.nextDoubleBetween(30000.0, 150000.0)

334

active <- Random.nextBoolean

335

nameLen <- Random.nextIntBetween(5, 15)

336

name <- Random.nextString(nameLen)

337

} yield TestUser(id, name, age, salary, active)

338

}

339

340

// Simulation with seeded random

341

val reproducibleSimulation = for {

342

_ <- Random.setSeed(12345) // Set seed for reproducibility

343

values <- ZIO.foreach(1 to 10)(_ => Random.nextGaussian)

344

mean = values.sum / values.length

345

_ <- Console.printLine(s"Mean of gaussian values: $mean")

346

} yield mean

347

348

// Random delay for chaos engineering

349

val chaosDelay = for {

350

delayMs <- Random.nextIntBetween(100, 5000)

351

_ <- Console.printLine(s"Adding chaos delay: ${delayMs}ms")

352

_ <- Clock.sleep(delayMs.millis)

353

} yield ()

354

355

// Random sampling

356

def sampleFromList[A](list: List[A], sampleSize: Int): UIO[List[A]] = {

357

for {

358

shuffled <- Random.shuffle(list)

359

} yield shuffled.take(sampleSize)

360

}

361

362

val samplingExample = for {

363

allUsers <- fetchAllUsers()

364

sampleUsers <- sampleFromList(allUsers, 10)

365

_ <- Console.printLine(s"Sampled ${sampleUsers.length} users for testing")

366

} yield sampleUsers

367

```

368

369

### System Service

370

371

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

372

373

```scala { .api }

374

/**

375

* Service for system environment and properties access

376

*/

377

trait System {

378

/** Get environment variable */

379

def env(variable: => String): IO[SecurityException, Option[String]]

380

381

/** Get environment variable with fallback */

382

def envOrElse(variable: => String, alt: => String): IO[SecurityException, String]

383

384

/** Get environment variable with optional fallback */

385

def envOrOption(variable: => String, alt: => Option[String]): IO[SecurityException, Option[String]]

386

387

/** Get all environment variables */

388

def envs: IO[SecurityException, Map[String, String]]

389

390

/** Get system property */

391

def property(prop: => String): IO[Throwable, Option[String]]

392

393

/** Get system property with fallback */

394

def propertyOrElse(prop: => String, alt: => String): IO[Throwable, String]

395

396

/** Get system property with optional fallback */

397

def propertyOrOption(prop: => String, alt: => Option[String]): IO[Throwable, Option[String]]

398

399

/** Get all system properties */

400

def properties: IO[Throwable, Map[String, String]]

401

402

/** Get system line separator */

403

def lineSeparator: UIO[String]

404

}

405

406

// Static access methods

407

object System {

408

def env(variable: => String): IO[SecurityException, Option[String]]

409

def envs: IO[SecurityException, Map[String, String]]

410

def property(prop: => String): Task[Option[String]]

411

def properties: Task[Map[String, String]]

412

def lineSeparator: UIO[String]

413

414

/** Current operating system */

415

lazy val os: OS

416

417

sealed trait OS {

418

def isWindows: Boolean

419

def isMac: Boolean

420

def isUnix: Boolean

421

def isSolaris: Boolean

422

def isUnknown: Boolean

423

}

424

}

425

```

426

427

**Usage Examples:**

428

429

```scala

430

import zio._

431

432

// Configuration from environment

433

val loadConfig = for {

434

port <- System.env("SERVER_PORT").map(_.getOrElse("8080"))

435

host <- System.env("SERVER_HOST").map(_.getOrElse("localhost"))

436

dbUrl <- System.env("DATABASE_URL").someOrElse(

437

ZIO.fail("DATABASE_URL environment variable is required")

438

)

439

debug <- System.env("DEBUG").map(_.contains("true"))

440

_ <- Console.printLine(s"Server config: $host:$port, DB: $dbUrl, Debug: $debug")

441

} yield AppConfig(host, port.toInt, dbUrl, debug)

442

443

// System information gathering

444

val systemInfo = for {

445

javaVersion <- System.property("java.version").map(_.getOrElse("unknown"))

446

osName <- System.property("os.name").map(_.getOrElse("unknown"))

447

osVersion <- System.property("os.version").map(_.getOrElse("unknown"))

448

userHome <- System.property("user.home").map(_.getOrElse("unknown"))

449

tmpDir <- System.property("java.io.tmpdir").map(_.getOrElse("/tmp"))

450

lineSep <- System.lineSeparator

451

452

_ <- Console.printLine("=== System Information ===")

453

_ <- Console.printLine(s"Java Version: $javaVersion")

454

_ <- Console.printLine(s"OS: $osName $osVersion")

455

_ <- Console.printLine(s"User Home: $userHome")

456

_ <- Console.printLine(s"Temp Dir: $tmpDir")

457

_ <- Console.printLine(s"Line Separator: ${lineSep.replace("\n", "\\n").replace("\r", "\\r")}")

458

} yield ()

459

460

// Platform-specific behavior

461

val platformSpecific = for {

462

_ <- if (System.os.isWindows) {

463

Console.printLine("Running Windows-specific code")

464

} else if (System.os.isUnix) {

465

Console.printLine("Running Unix-specific code")

466

} else {

467

Console.printLine("Running generic code")

468

}

469

pathSep <- System.property("path.separator").map(_.getOrElse(":"))

470

_ <- Console.printLine(s"Path separator: $pathSep")

471

} yield ()

472

473

// Environment variable validation

474

val validateEnvironment = for {

475

requiredVars <- ZIO.succeed(List("DATABASE_URL", "API_KEY", "LOG_LEVEL"))

476

477

missing <- ZIO.foldLeft(requiredVars)(List.empty[String]) { (acc, varName) =>

478

System.env(varName).map {

479

case Some(_) => acc

480

case None => varName :: acc

481

}

482

}

483

484

_ <- ZIO.when(missing.nonEmpty) {

485

Console.printLineError(s"Missing required environment variables: ${missing.mkString(", ")}") *>

486

ZIO.fail("Environment validation failed")

487

}

488

489

_ <- Console.printLine("Environment validation passed")

490

} yield ()

491

492

// Dynamic property loading

493

def loadPropertiesFromFile(filename: String): Task[Map[String, String]] = {

494

for {

495

userHome <- System.property("user.home").someOrFail(new RuntimeException("user.home not set"))

496

configPath = s"$userHome/$filename"

497

_ <- Console.printLine(s"Loading properties from: $configPath")

498

// Implementation would read from file

499

properties <- ZIO.succeed(Map("app.name" -> "MyApp", "app.version" -> "1.0"))

500

} yield properties

501

}

502

503

// Environment-based feature flags

504

val featureFlags = for {

505

allEnvVars <- System.envs

506

flags = allEnvVars.collect {

507

case (key, value) if key.startsWith("FEATURE_") =>

508

key.stripPrefix("FEATURE_") -> value.toBoolean

509

}

510

_ <- Console.printLine(s"Feature flags: $flags")

511

} yield flags

512

```

513

514

### Service Testing and Mocking

515

516

Patterns for testing services and providing mock implementations.

517

518

```scala { .api }

519

// Mock console for testing

520

class TestConsole(

521

inputs: Ref[List[String]],

522

outputs: Ref[List[String]],

523

errors: Ref[List[String]]

524

) extends Console {

525

526

def print(line: => Any): IO[IOException, Unit] =

527

outputs.update(_ :+ line.toString).unit

528

529

def printLine(line: => Any): IO[IOException, Unit] =

530

outputs.update(_ :+ (line.toString + "\n")).unit

531

532

def printError(line: => Any): IO[IOException, Unit] =

533

errors.update(_ :+ line.toString).unit

534

535

def printLineError(line: => Any): IO[IOException, Unit] =

536

errors.update(_ :+ (line.toString + "\n")).unit

537

538

def readLine: IO[IOException, String] =

539

inputs.modify {

540

case head :: tail => (head, tail)

541

case Nil => throw new IOException("No more input available")

542

}

543

544

def readLine(prompt: String): IO[IOException, String] =

545

print(prompt) *> readLine

546

}

547

548

// Test clock for deterministic timing

549

class TestClock(timeRef: Ref[Long]) extends Clock {

550

def currentTime(unit: TimeUnit): UIO[Long] =

551

timeRef.get.map(unit.convert(_, TimeUnit.MILLISECONDS))

552

553

def nanoTime: UIO[Long] =

554

timeRef.get.map(_ * 1_000_000)

555

556

def sleep(duration: Duration): UIO[Unit] =

557

timeRef.update(_ + duration.toMillis).unit

558

559

def currentDateTime: UIO[OffsetDateTime] =

560

timeRef.get.map(millis =>

561

OffsetDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneOffset.UTC)

562

)

563

564

// ... other methods

565

}

566

567

// Deterministic random for testing

568

class TestRandom(seedRef: Ref[Long]) extends Random {

569

def nextInt: UIO[Int] =

570

seedRef.updateAndGet(seed => (seed * 1103515245L + 12345L) & 0x7fffffffL).map(_.toInt)

571

572

def nextBoolean: UIO[Boolean] =

573

nextInt.map(_ % 2 == 0)

574

575

// ... other methods

576

}

577

```

578

579

**Usage Examples:**

580

581

```scala

582

// Service testing example

583

val testProgram = for {

584

inputs <- Ref.make(List("Alice", "30"))

585

outputs <- Ref.make(List.empty[String])

586

errors <- Ref.make(List.empty[String])

587

588

testConsole = new TestConsole(inputs, outputs, errors)

589

590

// Run program with test console

591

_ <- (for {

592

name <- Console.readLine("Name: ")

593

age <- Console.readLine("Age: ")

594

_ <- Console.printLine(s"Hello $name, you are $age years old")

595

} yield ()).provideService(testConsole)

596

597

// Verify outputs

598

allOutputs <- outputs.get

599

_ <- Console.printLine(s"Captured outputs: $allOutputs")

600

} yield ()

601

```