How to use ZIO Macros?
Scrapping the boilerplate with macros
Many libraries come together with usage best practices and repeatable code, ZIO is no different. Fortunately ZIO provides macros
to perform these repetitive tasks for you. At the moment these are only available for Scala versions 2.x
, however their equivalents
for Dotty are on our roadmap.
Prerequisites
To enable macro expansion you need to setup your project:
- for Scala
>= 2.13
add compiler option
scalacOptions += "-Ymacro-annotations"
- for Scala
< 2.13
add macro paradise compiler plugin
compilerPlugin(("org.scalamacros" % "paradise" % "2.1.1") cross CrossVersion.full)
Capability accessors
Installation
libraryDependencies += "dev.zio" %% "zio-macros" % "<zio-version>"
Description
The @accessible
macro generates capability accessors into annotated module object.
import zio.{ Has, ZIO }
import zio.macros.accessible
@accessible
object AccountObserver {
trait Service {
def processEvent(event: AccountEvent): UIO[Unit]
}
// below will be autogenerated
def processEvent(event: AccountEvent) =
ZIO.accessM[Has[AccountObserver.Service]](_.get[Service].processEvent(event))
}
For normal values, a ZIO
with Nothing
on error channel is generated.
If the code is throwing exceptions see @throwing
below.
import zio.{ Has, ZIO }
import zio.macros.accessible
@accessible
object Module {
trait Service {
def pureMethod(v: Something): SomethingElse
}
// below will be autogenerated
def pureMethod(v: Something): ZIO[Service, Nothing, SomethingElse] =
ZIO.access[Has[Module.Service]](_.get[Service].pureMethod(v))
}
The @throwing
annotation will mark impure methods.
Using this annotation will request ZIO to push the error on the error channel.
import zio.{ Has, ZIO }
import zio.macros.accessible
@accessible
object Module {
trait Service {
@throwing
def impureMethod(v: Something): SomethingElse
}
// below will be autogenerated
def impureMethod(v: Something): ZIO[Service, Throwable, SomethingElse] =
ZIO.accessM[Has[Module.Service]](s => ZIO(s.get[Service].impureMethod(v)))
}
Note: the macro can only be applied to objects which contain a trait called
Service
.
Capability tags
Installation
libraryDependencies += "dev.zio" %% "zio-test" % "<zio-version>"
Description
The @mockable[A]
generates capability tags and mock layer into annotated object.
import zio.test.mock.mockable
@mockable[AccountObserver.Service]
object AccountObserverMock
Will result in:
import zio.{ Has, UIO, URLayer, ZLayer }
import zio.test.mock.{ Mock, Proxy }
object AccountObserverMock extends Mock[Has[AccountObserver.Service]] {
object ProcessEvent extends Effect[AccountEvent, Nothing, Unit]
object RunCommand extends Effect[Unit, Nothing, Unit]
val compose: URLayer[Has[Proxy], AccountObserver] =
ZLayer.fromServiceM { proxy =>
withRuntime.map { rts =>
new AccountObserver.Service {
def processEvent(event: AccountEvent) = proxy(ProcessEvent, event)
def runCommand: UIO[Unit] = proxy(RunCommand)
}
}
}
}