Actually, you don't want an alias for a type, you want an alias for a bound.
Please see How to avoid duplication of type bound in Scala
Briefly, you should keep F-bounds wherever you need them. Actually, this is not a code duplication. Type parameter T
of Trait
, A
, B
are actually three different type parameters, which can have different bounds.
But theoretically you can abbreviate bounds with a macro annotation, although this is not worth it and generally can be dangerous, because this can be surprising for your team mates, can make debugging more complicated and can confuse your IDE
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
@compileTimeOnly("enable macro annotations")
class fbound extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro FBoundMacro.impl
}
object FBoundMacro {
def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
def addFBound(tparam: Tree): Tree = tparam match {
case q"$mods type $name[..$tparams] >: $low <: $high" =>
val tparamsNames = tparams.map {
case q"$_ type $nme[..$_] >: $_ <: $_" => nme
}
val fBound = tq"Trait[$name[..$tparamsNames]]"
val high1 = high match {
case tq"$EmptyTree" => fBound
case tq"..$withTypes { ..$refinements }" =>
val withTypes1 = withTypes :+ fBound
tq"..$withTypes1 { ..$refinements }"
case tq"$typ" => tq"$typ with $fBound"
}
q"$mods type $name[..$tparams] >: $low <: $high1"
}
annottees match {
case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: tail =>
val tparams1 = addFBound(tparams.head) :: tparams.tail
q"""
$mods class $tpname[..$tparams1] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }
..$tail
"""
}
}
}
Usage:
trait Trait[T <: Trait[T]] {
def merge(t: T): T
}
@fbound case class A[T](t: T, i: Int)
@fbound case class B[T](t: T, str: String)
//scalac: {
// case class A[T <: Trait[T]] extends scala.Product with scala.Serializable {
// <caseaccessor> <paramaccessor> val t: T = _;
// <caseaccessor> <paramaccessor> val i: Int = _;
// def <init>(t: T, i: Int) = {
// super.<init>();
// ()
// }
// };
// ()
//}
//scalac: {
// case class B[T <: Trait[T]] extends scala.Product with scala.Serializable {
// <caseaccessor> <paramaccessor> val t: T = _;
// <caseaccessor> <paramaccessor> val str: String = _;
// def <init>(t: T, str: String) = {
// super.<init>();
// ()
// }
// };
// ()
//}