2015-06-30 14 views
9

Sto tentando di scrivere una macro che accetta una classe con un'interfaccia java bean e una classe case e crea una coppia di metodi per la mappatura tra di essi.Test se c.universe.Type è assegnabile a un altro tipo in una macro

Sto tentando di verificare che i tipi corrispondano per ogni proprietà, tuttavia i tipi nel bean java sono ad es. java.lang.Long e i tipi nella classe case sono scala.Long.

La mia domanda è, dato l'oggetto c.universe.Type per questi 2, c'è un modo per verificare se ci sono conversioni implicite tra di loro? per testare se riesco a passare a un metodo che si aspetta l'altro.

+0

Hai mai trovare una soluzione per questo? – Marcin

+0

Sfortunatamente no, ho finito con una mappa hardcoded dei tipi di base (quelli erano quelli di cui ero veramente preoccupato). –

+0

Sì, sto facendo lo stesso. Molto triste; ( – Marcin

risposta

0

Se si desidera verificare se esiste una conversione implicita, è possibile utilizzare c.inferImplicitView.

Proof of concept:

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

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

def test[T,S,R](value: T, func: S => R): R = macro Macro.myMacro[T,S,R] 

object Macro { 
    def myMacro[T: c.WeakTypeTag,S: c.WeakTypeTag,R](c: Context)(value: c.Tree, func: c.Tree): c.Tree = { 
    import c.universe._ 
    val view = c.inferImplicitView(value, weakTypeOf[T], weakTypeOf[S]) 
    if (view == EmptyTree) 
     c.abort(c.enclosingPosition, "Cannot apply function") 
    else 
     q"$func($value)" 
    } 
} 

// Exiting paste mode, now interpreting. 

import scala.reflect.macros.blackbox.Context 
import scala.language.experimental.macros 
defined term macro test: [T, S, R](value: T, func: S => R)R 
defined object Macro 

scala> test(3L, (l: java.lang.Long) => l.toString) 
res20: String = 3 

scala> test(3L, (l: java.lang.Integer) => l.toString) 
<console>:23: error: Cannot apply function 
     test(3L, (l: java.lang.Integer) => l.toString) 
     ^

Se non si dispone di un value, a quanto pare funziona anche se si fa c.inferImplicitView(EmptyTree, weakTypeOf[T], weakTypeOf[S]).


Un esempio più complesso più vicina al vero problema:

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

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

def mapBetween[JB,CC](jb: JB): CC = macro Macro.myMacro[JB,CC] 

object Macro { 
    def myMacro[JB: c.WeakTypeTag, CC: c.WeakTypeTag](c: Context)(jb: c.Tree): c.Tree = { 
    import c.universe._ 
    val jbTpe = weakTypeOf[JB] 
    val ccTpe = weakTypeOf[CC] 
    val constructor = ccTpe.members.filter(m => 
     m.isConstructor && m.name != TermName("$init$") 
    ).head.asMethod 
    if(constructor.paramLists.size != 1 || constructor.paramLists.head.size != 1) 
     c.abort(c.enclosingPosition, "not supported :(") 
    val ccParam = constructor.paramLists.head.head 
    val ccParamType = ccParam.typeSignature 
    val ccParamName = ccParam.name.toString 

    val jbGetter = jbTpe.member(TermName(s"get${ccParamName.head.toUpper + ccParamName.tail}")) 
    val getterType = jbGetter.asMethod.returnType 

    val view = c.inferImplicitView(EmptyTree, getterType, ccParamType) 
    if (view == EmptyTree) 
     c.abort(c.enclosingPosition, "Cannot apply function") 
    else 
     q"new ${ccTpe.typeSymbol.name.toTypeName}($jb.${jbGetter.name.toTermName})" 
    } 
} 

// Exiting paste mode, now interpreting. 

import scala.reflect.macros.blackbox.Context 
import scala.language.experimental.macros 
defined term macro mapBetween: [JB, CC](jb: JB)CC 
defined object Macro 

scala> case class CaseClass(foo: Int) 
defined class CaseClass 

scala> class JavaBean{ def getFoo(): java.lang.Integer = 42 } 
defined class JavaBean 

scala> mapBetween[JavaBean,CaseClass](new JavaBean) 
res0: CaseClass = CaseClass(42) 

scala> case class CaseClass(foo: Int) 
defined class CaseClass 

scala> class JavaBean{ def getFoo(): java.lang.Double = 42.0 } 
defined class JavaBean 

scala> mapBetween[JavaBean,CaseClass](new JavaBean) 
<console>:27: error: Cannot apply function 
     mapBetween[JavaBean,CaseClass](new JavaBean) 
            ^
+0

Funziona per i parametri della macro in quanto tale, ma non funzionerà per i membri riflessi delle classi sotto controllo. – Marcin

+0

@Marcin Perché no? La domanda dice che hai oggetti 'Tipo' che rappresentano tutti i tipi che 'weakTypeOf [T]' è solo un modo per ottenere un oggetto 'Type'. –

+0

No non lo è. Stai scegliendo di risolvere un problema più semplice Se pensi che io abbia torto, dimostrami che torto usando questa tecnica per risolvere il vero problema dichiarato - confrontando i tipi di membri all'interno delle classi – Marcin