公式のドキュメントや他の人の書いたブログ記事を読みつつ書いてみたのでメモ。
import zio.{IO, RIO, Runtime, Task, UIO, URIO, ZIO} // Userリポジトリのインタフェース case class User(name: String) object UserRepo { trait Service { def load(): Task[Option[User]] def store(user: User): Task[Unit] } def load: RIO[UserRepo.Service, Option[User]] = ZIO.accessM { env => env.load() } def store(user: User): RIO[UserRepo.Service, Unit] = ZIO.accessM { env => env.store(user) } } // ロガーのインタフェース object Logger { trait Service { def info(msg: String): UIO[Unit] def error(msg: String): UIO[Unit] } def info(msg: String): URIO[Logger.Service, Unit] = ZIO.accessM { env => env.info(msg) } def error(msg: String): URIO[Logger.Service, Unit] = ZIO.accessM { env => env.error(msg) } }
以上のインタフェースの実装を踏まえて、実装を用意する。
各実装は、上で完成したインターフェイスオブジェクト内部のServiceトレイトを実装している。
// オンメモリなUserリポジトリの実装(ただのモックだけど) trait MemoryUserRepo extends UserRepo.Service { override def load(): Task[Option[User]] = IO.succeed(Some(User("seiya"))) override def store(user: User): Task[Unit] = IO.succeed(()) } // コンソール用ロガーの実装 trait ConsoleLogger extends Logger.Service { override def info(msg: String): UIO[Unit] = IO.succeed(println(s"[INFO] $msg")) override def error(msg: String): UIO[Unit] = IO.succeed(println(s"[ERROR] $msg")) }
最後に依存する実装をミクスインしたDepsをアプリケーションに与えて起動する
object Deps extends MemoryUserRepo with ConsoleLogger object ZIOApp extends App { val usecase = for { _ <- Logger.info("start") userM <- UserRepo.load _ <- userM.fold(Logger.error("no user"))( user => Logger.info(s"name: ${user.name}") ) _ <- Logger.info("done") } yield () Runtime.default.unsafeRun(usecase.provide(Deps)) }
2020年2月以降、ZIOには実装の依存関係を縦(Vertical)と横(Horizontal)の軸で合成できるようにするZLayerという機能が実装されたらしい。
ZLayer is a simple, declarative recipe for building a service in terms of its dependencies.
— John A De Goes (@jdegoes) 2020年2月18日
It's an edge in your application's dependency graph, expressed as a value, with type-safe, composable operators that let you build big graphs out of parts. 1/4
自分も手元でZLayerを用いた実装を試してみようと思ったが、Scala力が足りないせいかコンパイルエラーが解消できなくて諦めた。
なので、このサンプル自体は動きはするがZIOのチカラが100%活かせているというわけではない。