组合 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) 不是有效语法。

AB必须封号

尝试

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 valdef 这样的东西,如果您 运行 可能会失败它们和 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
}