CI | Sonatype | Issue Resolution |
---|---|---|
Zio-delegate has been merged into Zio-Macros. This repo will no longer be maintained.
This package defines an annotation and a typeclass that simplify working with mixins and proxies in scala.
Add this dependency to your build.sbt
"dev.zio" %% "zio-delegate" % "0.0.5"
If using a scala version < 2.13 you'll also need to add the macro paradise compiler plugin.
compilerPlugin(("org.scalamacros" % "paradise" % "2.1.1") cross CrossVersion.full)
If using scala 2.13 you need to add the macro annotation compiler option.
-Ymacro-annotations
This annotation can only be used on a constructur parameter in a class definition. This will do a number of things to the resulting class definitions:
- The class will additionally extend any traits extended by the annotated member.
import zio.delegate._
trait Foo {
def foo: Int = 4
}
object FooImpl extends Foo
class Bar(@delegate f: Foo)
val b: Foo = new Bar(FooImpl)
- Any methods on the resulting type of the defined class that are also defined on the annotated member will be forwarded to the member unless a definition exists in the body of the class.
import zio.delegate._
trait Foo {
def foo: Int
}
abstract class Foo1 extends Foo {
def foo = 4
def foo1: Int
}
class Bar(@delegate f: Foo)
println(new Bar(new Foo {}).foo) // 4
class Bar1(@delegate f: Foo) {
def foo = 3
}
println(new Bar1(new Foo {}).foo) // 3
// classes have to be explicitly extended. Forwarders will still
// be automatically generated though.
class Bar2(@delegate f: Foo1) exends Foo1
println(new Bar1(new Foo1 { def foo1 = 3 }).foo1) // 3
- The behavior of the annotation can be customized with three options
class delegate(verbose: Boolean = false, forwardObjectMethods: Boolean = false, generateTraits: Boolean = true)
- verbose: The generated class will be reported during compilation. This is very useful for debugging behavior of the annotation or getting a feel for the generated code.
- forwardObjectMethods: controls whether methods defined on Object and similiar classes should be forwarded. The list of methods affected by this is currently:
Set( "java.lang.Object.clone", "java.lang.Object.hashCode", "java.lang.Object.finalize", "java.lang.Object.equals", "java.lang.Object.toString", "scala.Any.getClass" )
- generateTraits: Whether the class should be adopted to automatically extend any traits defined on the annotated member. If set to false only methods of traits / classes that are explicitly extended will be forwarded.
An instance of
trait Mix[A, B] {
def mix(a: A, b: B): A with B
}
provides evidence that an instance of B
can be mixed into an instance of A
.
A macro is defined that can derive an instance of Mix for any two types if the first is a nonfinal class or a trait and the second one is a trait. It can be used like this
class Foo {
def foo: Int = 2
}
trait Bar {
def bar: Int
}
def withBar[A](a: A)(implicit ev: Mix[A, Bar]): A with Bar = {
ev.mix(a, new Bar { def bar = 2 })
}
withBar[Foo](new Foo()).bar // 2
Definitions in the second type will override implementations in the first type.
One of the primary motivations for writing this library was more comfortable incremental building of ZIO environment cakes. A possible way of doing this is:
import zio.delegate._
import zio.blocking.Blocking
import zio.clock.Clock
trait Sys extends Serializable {
def sys: Sys.Service[Any]
}
object Sys {
trait Service[R] extends Serializable
trait Live extends Sys { self: Clock with Blocking =>
def sys = new Service[Any] {}
}
def withSys[A <: Clock with Blocking](a: A)(implicit ev: A Mix Sys): A with Sys = {
class SysInstance(@delegate underlying: Clock with Blocking) extends Live
ev.mix(a, new SysInstance(a))
}
}
This is heavily inspired by adamw/scala-macro-aop and b-studios/MixinComposition. Make sure to check out the projects!