Объедините экземпляры 2 черт, чтобы сформировать экземпляр третьей черты - PullRequest
1 голос
/ 04 апреля 2020

Аналогично Использование бесформенного scala для объединения полей двух разных классов дел Учитывая следующие черты

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

Кажется, если бы это были все классы дел, я мог бы сделать это с бесформенными Generi c

Ответы [ 2 ]

2 голосов
/ 04 апреля 2020

Это не может быть сделано обычной функцией. С классами наблюдений вы знаете, что вы просто храните значения и что эти значения уже вычислены, они не являются чем-то вроде lazy val или def, что может привести к сбою, если вы их запустите, и бесформенное может основываться на этом предположении, чтобы смело упакуйте эти значения в некоторые другие структуры, которые легко объединить.

Если вы хотите сделать то же самое с чертами, и вернуть немного A with B, вам, по сути, придется построить макрос, который принимает A instance, B instance, и объединяет их - что будет иметь много угловых случаев (например, что, если обе черты имеют одинаковые имена методов / значений, но с разными типами результатов? Или что, если обе имеют одинаковое значение с одинаковыми name - что следует использовать?).

К сожалению, я не могу найти такой макрос нигде, поэтому его нужно было бы написать. Большинство людей просто сливают эти черты вручную так:

val ab: A with B = new (A with B) {
  override val a: String = a.a
  override val b: String = b.b
}
1 голос
/ 04 апреля 2020

Бесформенный не может этого сделать. Вам нужен макрос.

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]
...