How do I inherit generic type from parent mixin type? For example I have a trait Foo
with one generic type A
:
trait Foo[A] {
def value: A
}
I have a class User
that uses Foo[String]
, like:
class User extends Foo[String] {
override def value: String = ???
}
Everything works fine. Now, I want to add a trait Bar[A]
with self-type of Foo[A]
.
trait Bar[A] { self: Foo[A] =>
def anotherValue: A
}
If I want to use Bar
in User
, I'll need to do:
class User extends Foo[String] with Bar[String] {
override def value: String = ???
override def anotherValue: String = ???
}
Is there anyway that I can simplify User
to this? (Bar
automatically infers type from corresponding Foo
.)
class User extends Foo[String] with Bar
You can define intermediate trait
trait UserLike[A] extends Foo[A] with Bar[A]
class User extends UserLike[String] {
override def value: String = ???
override def anotherValue: String = ???
}
You can define a macro annotation but this would be an overkill
@extendsBar
class User extends Foo[String] {
override def value: String = ???
override def anotherValue: String = ???
}
//scalac: {
// class User extends Foo[String] with Bar[String] {
// def <init>() = {
// super.<init>();
// ()
// };
// override def value: String = $qmark$qmark$qmark;
// override def anotherValue: String = $qmark$qmark$qmark
// };
// ()
//}
import scala.annotation.StaticAnnotation
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
class extendsBar extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro ExtendsBarMacro.impl
}
object ExtendsBarMacro {
def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
annottees match {
case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: tail =>
val fooArg = parents.collectFirst {
case tq"Foo[$t]" => t
}.getOrElse(c.abort(c.enclosingPosition, "class must extend Foo"))
val parents1 = parents :+ tq"Bar[$fooArg]"
q"""
$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents1 { $self => ..$stats }
..$tail
"""
case _ =>
c.abort(c.enclosingPosition, "annottee must be a class")
}
}
}
This would be more flexible if A
were a type member rather than type parameter
trait Foo {
type A
def value: A
}
trait Bar { self: Foo =>
def anotherValue: A
}
class User extends Foo with Bar {
override type A = String
override def value: String = ???
override def anotherValue: String = ???
}