2016-10-04 10 views
0

ジェネリック型の暗黙のパラメータにアクセスしようとしています。 Scalaは明示的なジェネリック型(例えば、printGenericType[Person])を持つメソッドを以下のように呼び出すことで、単純な場合でも暗黙的に罰金を見つけることができます。実行時に明示的に暗黙のパラメータにアクセスする

ただし、TypeTag[Person]を作成してprintGenericTypeGivenTypeTagに渡すと、Scalaは暗黙的なパラメータを見つけることができません。私が正しく理解していれば

case class Person(name: String) 
case class Animal(age: Int) 
implicit val p = Person("cory") 
implicit val a = Animal(2) 

def main(args: Array[String]): Unit = { 
    // Can find the implicit Person, prints "Hello Person(cory)" 
    printGenericType[Person] 

    // Can find the implicit Animal, prints "Hello Animal(2)" 
    printGenericType[Animal] 

    // See comment below 
    printNamedType("Person") 
    printNamedType("Animal") 
} 

def printGenericType[T](implicit t: T) = { 
    println(s"Hello $t") 
} 

def printGenericTypeGivenTypeTag[T](typeTag: TypeTag[T])(implicit t: T) = { 
    println(s"Hello $t") 
} 

def printNamedType[T](name: String) = { 
    val typetag: TypeTag[T] = getTypeTag[T](name) 

    // Cannot find the implicit of type T, compiler error 
    printGenericTypeGivenTypeTag(typetag) 
} 

def getTypeTag[T](name:String): TypeTag[T] = ... //Implementation irrelevant 

は、Scalaはコンパイル時に暗黙のパラメータを見つけ、それはそれはコンパイル時にジェネリック型Tのための暗黙のパラメータを見つけることができないという意味になります。

しかし、私はTの暗黙のインスタンスが存在することを知っています。 Tの暗黙の値を見つけるようにprintGenericTypeGivenTypeTagを書き換えることはできますか?実行時には、このメソッドは実際のタイプのTにアクセスできるため、スコープ内にある同じタイプの暗黙のパラメータを見つけることができるようです。好奇心のために

は、この背後にある理由は、これを避けるためです:

name match { 
    case "Person" => printGenericType[Person] 
    case "Animal" => printGenericType[Animal] 
} 

答えて

1

は、あなたが本当に暗黙的Tに合格したいとしていない

質問に答えるためにではなく、TypeTagTではありません。ここに私の言いたいことがあります。暗黙のバリュークラスを使うほうが良いでしょう。今、あなたはケースクラスを印刷しようとしている場合、私はおそらく利便性のために、暗黙的なマクロルートを下るだろう本当の問題に

を解決

implicit class GenericPrinter[T](val obj: T) extends AnyVal { 
    def printGenericType()(implicit tag: TypeTag[T]) = { 
    // do stuff with the tag 
    Console.println("Hello $obj") 
    } 
} 
val x: Person = Person(...) 
x.printGenericType 

。私たちのためにこれを行うマクロを書くことは、例えばcase classのすべてのコンストラクタパラメータに基づいてデバッグ文字列を出力することは、とても簡単です。

trait DeepPrinter[T <: Product with Serializable] { 

    /** 
    * Prints a deeply nested debug string for a given case class. 
    * This uses implicit macros to materialise the printer type class. 
    * In English, when we request for an implicit printer: DeepPrinter[T], 
    * the pre-defined compile time macro will generate this method for us 
    * based on the fields of the given case class. 
    * 
    * @param sep A separator to use to delimit the rows in a case class. 
    * @return A fully traced debug string so we can see how a case class looks like. 
    */ 
    def debugString(sep: String = "\n"): String 
} 

object DeepPrinter { 
    implicit def deepPrinter[T <: Product with Serializable] = macro DeepPrinterImpl.deepPrinterImpl[T] 
} 

マクロはかなり簡単ですが、このように見えます。

import language.experimental.macros 
import scala.reflect.macros.blackbox 

@macrocompat.bundle 
class DeepPrinterImpl(val c: blackbox.Context) { 

    import c.universe._ 

    object CaseField { 
    def unapply(symbol: TermSymbol): Option[(Name, Type)] = { 
     if (symbol.isVal && symbol.isCaseAccessor) { 
     Some(symbol.name -> symbol.typeSignature) 
     } else { 
     None 
     } 
    } 
    } 

    def fields(tpe: Type): Iterable[(Name, Type)] = { 
    tpe.decls.collect { case CaseField(nm, tpeSn) => nm -> tpeSn } 
    } 

    def materialize[T : c.WeakTypeTag]: c.Expr[DeepPrinter[T]] = { 
    val tpe = weakTypeOf[T] 
    val (names, types) = fields(tpe).unzip 

    // change the package name to the correct one here! 
    val tree = q""" 
     new com.bla.bla.DeepPrinter[$tpe] { 
     def debugString(sep: String = "\n") = Seq(..$names).mkString(sep) 
     } 
    """ 
    c.Expr[DeepPrinter[T]](tree) 
    } 
} 
関連する問題