2013-07-20 9 views
9

Sto provando a verificare se due "contenitori" utilizzano lo stesso tipo di tipo superiore. Guardate il seguente codice:Come testare la conformità dei tipi di tipo superiore in Scala

import scala.reflect.runtime.universe._ 

class Funct[A[_],B] 

class Foo[A : TypeTag](x: A) { 
    def test[B[_]](implicit wt: WeakTypeTag[B[_]]) = 
    println(typeOf[A] <:< weakTypeOf[Funct[B,_]]) 

    def print[B[_]](implicit wt: WeakTypeTag[B[_]]) = { 
    println(typeOf[A]) 
    println(weakTypeOf[B[_]]) 
    } 
} 

val x = new Foo(new Funct[Option,Int]) 

x.test[Option] 
x.print[Option] 

L'output è:

false 
Test.Funct[Option,Int] 
scala.Option[_] 

Tuttavia, mi aspetto che i test di conformità per avere successo. Che cosa sto facendo di sbagliato? Come posso testare per i tipi più alti?

Chiarimento

Nel mio caso, i valori sto testando (il x: A nell'esempio) sono disponibili in una List[c.Expr[Any]] in una macro. Quindi qualsiasi soluzione basata sulla risoluzione statica (come quella che ho dato), non risolverà il mio problema.

+1

Per restituire un po 'di attesa: 'implicitamente [Funz [Option, Int] <: pedrofurla

+0

Da https: //gist.github.com/xeno-by/6054650 Vedo che il tipo reifier non vede un tag type per B, quindi non si ottiene un tipo corretto da weakTypeOf [Funct [B, _]]. –

+0

Ok, ora capisco qual è il problema. È di nuovo il mix tra underscore usati nelle definizioni dei parametri di tipo e altrove. Il carattere di sottolineatura in "TypeTag [B [_]]" indica un tipo esistenziale, quindi si ottiene un tag type non per 'B', ma per un wrapper esistenziale su di esso. Di conseguenza 'typeOf [Funct [B, _]]' non può usare questo tag di tipo e si arrabbia. –

risposta

8

E 'il disguido tra sottolineatura utilizzato nelle definizioni dei parametri di tipo e altrove. Il carattere di sottolineatura in TypeTag[B[_]] indica un tipo esistenziale, quindi si ottiene un tag non per B, ma per un wrapper esistenziale su di esso, che è praticamente inutile senza post-elaborazione manuale.

Di conseguenza typeOf[Funct[B, _]] che necessita di un tag per raw B non può utilizzare il tag per il wrapper e si arrabbia. Rimanendo sconvolto intendo che si rifiuta di unire il tag in scope e fallisce con un errore di compilazione. Se si utilizza invece weakTypeOf, questo avrà esito positivo, ma genererà stub per tutto ciò che non può essere unito, rendendo il risultato inutile per i controlli di sottotitoli.

Sembra che in questo caso siamo davvero nei limiti di Scala nel senso che non è possibile per noi fare riferimento a B in WeakTypeTag[B], perché non abbiamo il polimorfismo gentile in Scala. Speriamo che qualcosa come DOT ci salvi da questo inconveniente, ma nel frattempo puoi usare questa soluzione alternativa (non è carina, ma non sono riuscito a trovare un approccio più semplice).

import scala.reflect.runtime.universe._ 

object Test extends App { 
    class Foo[B[_], T] 
    // NOTE: ideally we'd be able to write this, but since it's not valid Scala 
    // we have to work around by using an existential type 
    // def test[B[_]](implicit tt: WeakTypeTag[B]) = weakTypeOf[Foo[B, _]] 
    def test[B[_]](implicit tt: WeakTypeTag[B[_]]) = { 
    val ExistentialType(_, TypeRef(pre, sym, _)) = tt.tpe 

    // attempt #1: just compose the type manually 
    // but what do we put there instead of question marks?! 
    // appliedType(typeOf[Foo], List(TypeRef(pre, sym, Nil), ???)) 

    // attempt #2: reify a template and then manually replace the stubs 
    val template = typeOf[Foo[Hack, _]] 
    val result = template.substituteSymbols(List(typeOf[Hack[_]].typeSymbol), List(sym)) 
    println(result) 
    } 
    test[Option] 
} 

// has to be top-level, otherwise the substituion magic won't work 
class Hack[T] 

Un lettore attento avrà notato che ho usato WeakTypeTag nella firma di foo, anche se dovrei essere in grado di utilizzare TypeTag. Dopo tutto, chiamiamo foo su un Option che è un tipo ben educato, nel senso che non coinvolge parametri di tipo irrisolti o classi locali che pongono problemi per TypeTag s. Sfortunatamente, non è così semplice a causa di https://issues.scala-lang.org/browse/SI-7686, quindi siamo costretti a usare un tag debole anche se non dovremmo averne bisogno.

+0

Può un abstract su Foo? – Edmondo1984

5

Quanto segue è una risposta che funziona per l'esempio che ho fornito (e potrebbe aiutare gli altri), ma non si applica al mio caso (non semplificato).

rubare da suggerimento di @ pedrofurla, e utilizzando il tipo-classi:

trait ConfTest[A,B] { 
    def conform: Boolean 
} 

trait LowPrioConfTest { 
    implicit def ctF[A,B] = new ConfTest[A,B] { val conform = false } 
} 

object ConfTest extends LowPrioConfTest { 
    implicit def ctT[A,B](implicit ev: A <:< B) = 
    new ConfTest[A,B] { val conform = true } 
} 

e aggiungi a al Foo:

def imp[B[_]](implicit ct: ConfTest[A,Funct[B,_]]) = 
    println(ct.conform) 

Ora:

x.imp[Option] // --> true 
x.imp[List] // --> false 
Problemi correlati