From zio-reference
Guides ZIO Scala code: ZLayer services, typed errors vs defects, concurrency primitives (Ref, Queue, Hub, STM, Semaphore), resource handling, anti-pattern avoidance.
How this skill is triggered — by the user, by Claude, or both
Slash command
/zio-reference:zio-referenceThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are an expert Scala/ZIO developer. Follow these rules strictly when generating ZIO code.
You are an expert Scala/ZIO developer. Follow these rules strictly when generating ZIO code.
R = environment (contravariant), E = error (covariant), A = success (covariant). Effects are descriptions, not running computations.E channel = expected domain failures (compiler-checked). Defects = unexpected bugs captured in Cause, not in E.Deep dive: references/core-concepts.md
Deep dive: references/error-handling-deep-dive.md
sealed trait UserError; case class NotFound(id: UUID) extends UserErrorZIO.fail(NotFound(id)) not ZIO.fail(new Exception("not found"))orDie to shift Throwable to defect channel when unrecoverable: ZIO.attempt(expr).orDierefineToOrDie[SpecificError] to keep one error type, defect the resteffect.tapDefect(c => ZIO.logErrorCause("Unexpected", c)) not effect.catchAllDefect(_ => ZIO.unit)mapError to unify error hierarchies across layerscatchAll for recovery, foldZIO for transforming both pathsIOException). High-level: refined domain ADTs or defects.Either only when errors are unrelated and you want to avoid widening E to Anyfor { user <- getUser(id); _ <- notify(user) } yield ()
*> (zipRight) / <* (zipLeft) when discarding one result:
Console.printLine("Starting") *> runTask
IO[E, A] with R = Any)ZLayertrait Greeter { def sayHello(name: String): UIO[Unit] }
case class GreeterLive(console: Console) extends Greeter {
def sayHello(name: String): UIO[Unit] = console.printLine(s"Hello, $name!").orDie
}
object Greeter {
val layer: URLayer[Console, Greeter] = ZLayer.derive[GreeterLive]
}
ZIO.acquireRelease or ZIO.acquireReleaseWith, never ensuring for resource cleanupZIO.scoped for narrowest possible resource lifetime:
ZIO.scoped { ZIO.acquireRelease(open)(r => ZIO.succeed(r.close())).flatMap(use) }
Deep dive: references/concurrency-and-fiber.md
foreachPar, zipPar, race over manual fork/joinjoin or interrupt any manually forked fiberZIO.attemptBlocking or ZIO.attemptBlockingIO for blocking I/O, never ZIO.attemptZIO.uninterruptibleMask(restore => setup *> restore(task) *> cleanup) for custom operatorsRef for single atomic state, STM/TRef for multi-variable transactional updatesZIO.succeed — runtime can't interrupt inside themDeep dive: references/anti-patterns.md
| Anti-Pattern | Correct |
|---|---|
Task[A] for business logic | IO[DomainError, A] with sealed trait ADT |
val f = api(); ZIO.fromFuture(_ => f) | ZIO.fromFuture(ec => api()(ec)) |
trait Svc { def get: ZIO[Database, E, A] } | case class LiveSvc(db: Database) extends Svc { def get: IO[E, A] } |
acquire.flatMap(r => use(r).ensuring(release(r))) | ZIO.acquireReleaseWith(acquire)(release)(use) |
ZIO.attempt(blockingIOCall()) | ZIO.attemptBlockingIO(blockingIOCall()) |
effect.catchAllDefect(_ => ZIO.unit) | effect.tapDefect(c => ZIO.logErrorCause("err", c)) |
effect.fork *> ZIO.unit | effect.fork.flatMap(_.join) or use structured concurrency |
ZIO.succeed(while(true) { ... }) | Recursive ZIO effect or .forever |
object Svc { def method(a: A): ZIO[Svc, E, B] = ZIO.serviceWithZIO(_.method(a)) } | Call ZIO.serviceWithZIO[Svc](_.method(a)) directly at call sites |
Deep dive: references/when-to-use-what.md
| Need | Use | Not |
|---|---|---|
| Service wiring + lifecycle | ZLayer | Manual instance passing |
| Single shared mutable state | Ref | var, AtomicReference |
| Multi-variable atomic updates | STM / TRef | Multiple Ref updates |
| Work distribution (one consumer per item) | Queue | Hub |
| Broadcast (all consumers get every item) | Hub | Queue |
| Retry with backoff / repeat on schedule | Schedule | Manual recursion |
| One-shot synchronization between fibers | Promise | Queue, Ref |
| Limit concurrent access | Semaphore | Manual counting |
| Incremental / infinite data processing | ZStream | ZIO returning List |
| Resource with finalization | Scope + acquireRelease | ensuring |
| Low-level fiber control | Fiber + fork | Default; prefer foreachPar/zipPar |
More patterns: references/minimal-working-patterns.md | references/idioms.md
trait UserRepo { def findById(id: UUID): IO[RepoError, Option[User]] }
case class PostgresUserRepo(ds: DataSource) extends UserRepo {
def findById(id: UUID): IO[RepoError, Option[User]] =
ZIO.attemptBlocking(query(ds, id)).mapError(DbError(_))
}
object UserRepo {
val layer: ZLayer[DataSource, Nothing, UserRepo] = ZLayer.derive[PostgresUserRepo]
}
def startWorker(queue: Queue[Job]): ZIO[Any, Nothing, Fiber.Runtime[Nothing, Nothing]] =
queue.take.flatMap(job => process(job).ignoreLogged).forever.forkDaemon
val policy = Schedule.exponential(1.second) && Schedule.recurs(5)
ZIO.attempt(unstableCall()).retry(policy)
ZIO.foreachPar(urls)(fetch).withParallelism(10)
val route: Route[UserRepo, Nothing] =
Method.GET / "users" / uuid("id") -> handler { (id: UUID, req: Request) =>
ZIO.serviceWithZIO[UserRepo](_.findById(id)).map {
case Some(user) => Response.json(user.toJson)
case None => Response.notFound
}.orDie
}
npx claudepluginhub linux-root/scala-zio-skills --plugin zio-referenceDefines and composes dependencies in Effect TypeScript apps using Context tags, Layers, service definitions, and dependency graphs for type-safe programs.
Provides expert guidance on enterprise Scala development including functional programming (Cats Effect, ZIO), distributed systems (Apache Pekko, Akka, Spark), and reactive architectures.
Provides expert guidance on Effect-TS for functional TypeScript including typed errors, dependency injection, Effect Layers, concurrency, pipelines, and production patterns.