2015-11-04 13 views
5

considerano una funzione generica:come applicare tipo non generico al momento della compilazione

def genericFn[T](fn: T => Boolean): Unit = { 
    // do something involves T 
} 

è esso possibile limitare T (al momento della compilazione) per essere un tipo semplice, non un tipo come List[Int]?


il problema subalterno Voglio risolvere è qualcosa di simile:

var actorReceive: Receive = PartialFunction.empty 
def addCase[T](handler: T => Boolean): Unit = { 
    actorReceive = actorReceive orElse ({ 
     case msg: T => // call handle at some point, plus some other logic 
      handler(msg) 
    }) 
} 

la funzione addCase si tradurrebbe in allarme la cancellazione tipo, che potrebbe essere risolto richiedendo ClassTag come: def addCase[T: ClassTag](..., ma ClassTag ancora non può evitare le chiamate come:

addCase[List[Int]](_ => {println("Int"); true}) 
addCase[List[String]](_ => {println("String"); false}) 

actorReceive(List("str")) // will print "Int" 

il codice qui sopra stampare "Int" mentre non emette alcun avviso o errore, c'è una via d'uscita?

+3

Dato che hai a che fare con akka qui, ho scoperto che provare a fare messaggi "generici" sarà un grosso problema, specialmente se tipo di serializzazione sta succedendo. Ad un livello più generale, il tipo di Int e List [Int] sono entrambi *, quindi un singolo parametro di tipo senza altre restrizioni non li differenzia. Probabilmente sarebbe sufficiente una soluzione standard per la tipizzazione, oppure potresti usare qualcosa di più avanzato come il link dk14 fornito. – melps

+1

Puoi aggiungere protezione da 'List': http://stackoverflow.com/questions/15962743/using-context-bounds-negatively-to-ensure-type-class-instance-is-absent-from-s – dk14

+0

ma io Non sono sicuro di come generarlo su qualsiasi tipo polimorfo – dk14

risposta

5

Non esiste un modo per applicare questo valore nel sistema di tipo così com'è, senza riflessioni.

Il modo migliore per farlo sarebbe avere una classe tipo come NonEraseable[A], che fornisce la prova che un tipo non ha parametri di tipo che verrebbero cancellati in fase di esecuzione. Un implicito NonEraseable[A] in ambito dovrebbe significare che A non ha parametri di tipo. Visto che questi Sarebbe noioso per creare manualmente, una macro implicita può fare il lavoro:

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

trait NonEraseable[A] 

object NonEraseable { 

    implicit def ev[A]: NonEraseable[A] = macro evImpl[A] 

    def evImpl[A](c: Context)(implicit tt: c.WeakTypeTag[A]): c.Expr[NonEraseable[A]] = { 
     import c.universe._ 
     val tpe = weakTypeOf[A] 
     if(tpe.dealias.typeArgs.isEmpty) 
      c.Expr[NonEraseable[A]](q"new NonEraseable[$tpe] {}") 
     else 
      c.abort(c.enclosingPosition, s"$tpe contains parameters that will be erased at runtime.") 
    } 

} 

Caso d'uso:

def onlySimple[A : NonEraseable](value: A): Unit = println(value) 

scala> onlySimple(1) 
1 

scala> onlySimple(List(1, 2, 3)) 
<console>:13: error: List[Int] contains parameters that will be erased at runtime. 
     onlySimple(List(1, 2, 3)) 
       ^

Usando questo, si può imporre al momento della compilazione che un parametro di tipo A con un limite di contesto NonEraseable è il tipo di tipo desiderato. (Supponendo di non imbrogliare e creare manualmente un'istanza della classe tipo)

3

È possibile ottenere almeno a non riuscire a run-time come segue:

def addCase[T: ClassTag](handler: T => Boolean): Unit = 
    if (classTag[T].runtimeClass.getTypeParameters.nonEmpty) { 
    // throw an exception 
    } else { 
    // the main code 
    } 

fase di compilazione fallimento può essere ottenuto utilizzando un macro invece di una funzione (approssimativa, non testata):

def addCase[T](handler: T => Boolean): Unit = macro addCaseImpl 

def addCaseImpl[T: c.WeakTypeTag](c: Context)(handler: c.Expr[T => Boolean]): c.Expr[Unit] = 
    if (c.weakTypeOf[T].typeParams.nonEmpty) { 
    c.abort(c.enclosingPosition, "Generic types not allowed in addCase") 
    } else { 
    // generate code for main line 
    } 
+0

"+1", ma non è macro troppo hardcore per quello, non puoi nemmeno provarlo nello stesso modulo. Forse c'è qualche soluzione informe. Post scriptum Ho provato diverse prove basate su prove, ma non ha funzionato perché il supporto di scala per generici e di alto livello è piuttosto strano.Cose del genere sono permesse per qualche ragione: 'implicit def notSimple [T, M [_]] (t: M [T]) =" "; notSimple (9) 'La modifica del tipo di ritorno in' M [T] 'ha ucciso il compilatore. Come il ***** 9 è M [T]? :) – dk14

Problemi correlati