簡體   English   中英

需要類類型,但找到E(scala宏)

[英]Class type required but E found (scala macro)

我正在嘗試刪除我正在編寫的API中的一些樣板。

粗略地說,我的API當前如下所示:

def toEither[E <: WrapperBase](priority: Int)(implicit factory: (String, Int) => E): Either[E, T] = {
  val either: Either[String, T] = generateEither()
  either.left.map(s => factory(s, priority))
}

這意味着用戶必須為每個使用的E生成一個隱式工廠。 如果用戶提供的類型沒有正確的ctor參數,我希望將其替換為提供良好編譯錯誤的宏。

我有以下幾點:

object GenericFactory {
  def create[T](ctorParams: Any*): T = macro createMacro[T]

  def createMacro[T](c: blackbox.Context)(ctorParams: c.Expr[Any]*)(implicit wtt: WeakTypeType[T]): c.Expr[T] = {
    import c.universe._
    c.Expr[T](q"new $wtt(..$ctorParams)")
  }
}

如果我為此GenericFactory.create[String]("hey")提供實類型,則沒有問題,但是如果我提供了泛型類型: GenericFactory.create[E]("hey")則出現以下編譯錯誤: class type required by E found

我哪里出問題了? 或者如果我想要的東西不可能實現,那么我還能做些其他事情來減少用戶的工作量嗎?

抱歉,但我認為您無法使其正常運行。 問題是Scala(作為Java)使用類型擦除。 這意味着所有泛型類型只有一種類型(可能除了價值類型專長之外,這現在已經不重要了)。 這意味着該宏對於所有E僅擴展一次,而對於用戶提供的每個E專業化則僅擴展一次。 而且,沒有辦法表達出某種限制,即某些通用類型E必須具有帶有給定簽名的構造函數(如果有,則一開始就不需要宏)。 因此,顯然它無法工作,因為編譯器無法為通用類型E生成構造函數調用。 因此,編譯器說的是,要生成構造函數調用,它需要一個實型而不是通用E

換句話說,宏不是魔術工具。 使用宏只是在編譯器處理的早期重新編寫一段代碼的一種方法,但是隨后它將由編譯器以通常的方式進行處理。 而您的宏所做的就是重寫

GenericFactory.create[E]("hey")

用類似的東西

new E("hey")

如果僅在代碼中編寫該代碼,則會得到相同的錯誤(可能不會感到驚訝)。

我認為您無法避免使用隱式工廠。 您可能可以修改宏以為有效類型生成那些隱式工廠,但我認為您無法進一步改進代碼。


更新:隱式工廠和宏

如果您只有一個地方需要一種類型的構造函數,那么我認為您可以做到的最好(或者是我可以做到的最好):

旁注 :整個想法來自“隱式宏”文章

  1. 您定義StringIntCtor[T]類型類特征和將生成該特征的宏:
import scala.language.experimental.macros
import scala.reflect.macros._


trait StringIntCtor[T] {
  def create(s: String, i: Int): T
}


object StringIntCtor {
  implicit def implicitCtor[T]: StringIntCtor[T] = macro createMacro[T]


  def createMacro[T](c: blackbox.Context)(implicit wtt: c.WeakTypeTag[T]): c.Expr[StringIntCtor[T]] = {
    import c.universe._
    val targetTypes = List(typeOf[String], typeOf[Int])
    def testCtor(ctor: MethodSymbol): Boolean = {
      if (ctor.paramLists.size != 1)
        false
      else {
        val types = ctor.paramLists(0).map(sym => sym.typeSignature)
        (targetTypes.size == types.size) && targetTypes.zip(types).forall(tp => tp._1 =:= tp._2)
      }
    }

    val ctors = wtt.tpe.decl(c.universe.TermName("<init>"))
    if (!ctors.asTerm.alternatives.exists(sym => testCtor(sym.asMethod))) {
      c.abort(c.enclosingPosition, s"Type ${wtt.tpe} has no constructor with signature <init>${targetTypes.mkString("(", ", ", ")")}")
    }


    // Note that using fully qualified names for all types except imported by default are important here
    val res = c.Expr[StringIntCtor[T]](
      q"""
           (new so.macros.StringIntCtor[$wtt] {
               override def create(s:String, i: Int): $wtt = new $wtt(s, i)
             })
       """)

    //println(res) // log the macro
    res


  }
}
  1. 您使用該特征作為
class WrapperBase(val s: String, val i: Int)

case class WrapperChildGood(override val s: String, override val i: Int, val float: Float) extends WrapperBase(s, i) {
  def this(s: String, i: Int) = this(s, i, 0f)
}

case class WrapperChildBad(override val s: String, override val i: Int, val float: Float) extends WrapperBase(s, i) {
}

object EitherHelper {
  type T = String

  import scala.util._

  val rnd = new Random(1)

  def generateEither(): Either[String, T] = {
    if (rnd.nextBoolean()) {
      Left("left")
    }
    else {
      Right("right")
    }
  }


  def toEither[E <: WrapperBase](priority: Int)(implicit factory: StringIntCtor[E]): Either[E, T] = {
    val either: Either[String, T] = generateEither()
    either.left.map(s => factory.create(s, priority))
  }
}

現在,您可以執行以下操作:

val x1 = EitherHelper.toEither[WrapperChildGood](1)
println(s"x1 = $x1")
val x2 = EitherHelper.toEither[WrapperChildGood](2)
println(s"x2 = $x2")
//val bad = EitherHelper.toEither[WrapperChildBad](3) // compilation error generated by c.abort

它會打印

x1 =左(WrapperChildGood(左,1,0.0))
x2 =右(右)

如果要在許多不同的地方確保存在不同的構造函數,則需要使宏復雜得多,以生成具有從外部傳遞的任意簽名的構造函數調用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM