组合 2 个特征的实例以形成第三个特征的实例
Combine instances of 2 traits to form instance of third trait
类似于
鉴于以下特征
trait A {
val a: String
}
trait B {
val b: String
}
trait AB extends A with B
是否可以这样做?
val q = new A { val a = "a" }
val w = new B { val b = "b" }
val e = combine(A, B) // Returns type AB
好像如果都是大小写 类 那么我可以用无形泛型来做
Shapeless 无法做到这一点。你需要一个宏。
combine(A, B)
不是有效语法。
A
和B
必须封号
尝试
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
trait Combine[A, B] {
type Out
}
object Combine {
type Aux[A, B, Out0] = Combine[A, B] { type Out = Out0 }
def instance[A, B, Out0]: Aux[A, B, Out0] = new Combine[A, B] { type Out = Out0 }
def mkCombine[A, B]: Combine[A, B] = macro impl[A, B]
def impl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
val aType = weakTypeOf[A]
val bType = weakTypeOf[B]
def subclasses(typ: Type) = typ.typeSymbol.asClass.knownDirectSubclasses
val aSubclasses = subclasses(aType)
val bSubclasses = subclasses(bType)
val intersection = aSubclasses intersect bSubclasses
if (intersection.size == 1)
q"Combine.instance[$aType, $bType, ${intersection.head}]"
else
c.abort(c.enclosingPosition, s"intersection: $intersection")
}
}
sealed trait A
sealed trait B
trait AB extends A with B
val c = Combine.mkCombine[A, B]
implicitly[c.Out =:= AB]
普通函数无法做到这一点。对于案例 类,您知道您只是在存储值并且这些值已经计算出来,它们不是像 lazy val
或 def
这样的东西,如果您 运行 可能会失败它们和 shapeless 可以建立在这个假设之上,以安全地将这些值重新打包到其他一些易于组合的结构中。
如果你想对特征做同样的事情,return一些A with B
你基本上必须构建一个宏,它需要A
个实例,B
个实例,并将它们组合起来——这会有很多极端情况(比如,如果两个特征具有相同的 methods/values 名称但结果类型不同怎么办?或者如果两者具有相同的值和相同的名称怎么办 - 这应该被使用?)。
不幸的是,我在任何地方都找不到这样的宏,所以只好写了。大多数人会像这样手动合并这些特征:
val ab: A with B = new (A with B) {
override val a: String = a.a
override val b: String = b.b
}
类似于
trait A {
val a: String
}
trait B {
val b: String
}
trait AB extends A with B
是否可以这样做?
val q = new A { val a = "a" }
val w = new B { val b = "b" }
val e = combine(A, B) // Returns type AB
好像如果都是大小写 类 那么我可以用无形泛型来做
Shapeless 无法做到这一点。你需要一个宏。
combine(A, B)
不是有效语法。
A
和B
必须封号
尝试
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
trait Combine[A, B] {
type Out
}
object Combine {
type Aux[A, B, Out0] = Combine[A, B] { type Out = Out0 }
def instance[A, B, Out0]: Aux[A, B, Out0] = new Combine[A, B] { type Out = Out0 }
def mkCombine[A, B]: Combine[A, B] = macro impl[A, B]
def impl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
val aType = weakTypeOf[A]
val bType = weakTypeOf[B]
def subclasses(typ: Type) = typ.typeSymbol.asClass.knownDirectSubclasses
val aSubclasses = subclasses(aType)
val bSubclasses = subclasses(bType)
val intersection = aSubclasses intersect bSubclasses
if (intersection.size == 1)
q"Combine.instance[$aType, $bType, ${intersection.head}]"
else
c.abort(c.enclosingPosition, s"intersection: $intersection")
}
}
sealed trait A
sealed trait B
trait AB extends A with B
val c = Combine.mkCombine[A, B]
implicitly[c.Out =:= AB]
普通函数无法做到这一点。对于案例 类,您知道您只是在存储值并且这些值已经计算出来,它们不是像 lazy val
或 def
这样的东西,如果您 运行 可能会失败它们和 shapeless 可以建立在这个假设之上,以安全地将这些值重新打包到其他一些易于组合的结构中。
如果你想对特征做同样的事情,return一些A with B
你基本上必须构建一个宏,它需要A
个实例,B
个实例,并将它们组合起来——这会有很多极端情况(比如,如果两个特征具有相同的 methods/values 名称但结果类型不同怎么办?或者如果两者具有相同的值和相同的名称怎么办 - 这应该被使用?)。
不幸的是,我在任何地方都找不到这样的宏,所以只好写了。大多数人会像这样手动合并这些特征:
val ab: A with B = new (A with B) {
override val a: String = a.a
override val b: String = b.b
}