2015-09-01 8 views
6

Sto cercando di ridimensionare un po 'di codice e ho dovuto lavorare con tipi di tipo più alto. Il seguente esempio minimale funziona bene:Le informazioni di tipo vengono perse quando si lavora con oggetti di tipo alto in Scala

trait Builder[M[_]] { 
    def build[A]: M[A] 
    def buildPair[A, B]: (M[A], M[B]) = (build[A], build[B]) 
} 

class List[A] 

class BuilderList extends Builder[List] { 
    def build[A] = new List[A] 
} 

val l: List[String] = (new BuilderList).build[String] 

val ll: (List[String], List[Double]) = (new BuilderList).buildPair[String, Double] 

defined trait Builder 
defined class List 
defined class BuilderList 
l: List[String] = [email protected] 
ll: (List[String], List[Double]) = ([email protected],[email protected]) 

Se ora voglio applicare questo a un tipo con due argomenti di tipo, diciamo

class Map[K, V] 

Vorrei essere in grado di scrivere

trait BuilderMap[K] extends Builder[Map[K, _]] {...} 

ma ovviamente questo non funziona perché gli argomenti di tipo in Scala non sono al curry.

Ho trovato che il seguente trucco mi ha permesso di passare la compilazione:

trait PartialApplier[F[_, _], K] { 
    type PartiallyApplied[_] = F[K, _] 
} 

class BuilderMap[K] extends Builder[PartialApplier[Map, K]#PartiallyApplied] { 
    def build[V] = new Map[K, V] 
} 

Ma poi, qualche effetto di strano accade e non riesco a capire il motivo per cui:

scala> val m: Map[Int, String] = (new BuilderMap[Int]).build[String] 
m: Map[Int,String] = [email protected] 

scala> val mm: (Map[Int, String], Map[Int, Double]) = (new BuilderMap[Int]).buildPair[String, Double] 
<console>:21: error: type mismatch; 
found : (Map[Int, _], Map[Int, _]) 
required: (Map[Int,String], Map[Int,Double]) 
    val mm: (Map[Int, String], Map[Int, Double]) = (new BuilderMap[Int]).buildPair[String, Double] 

Sembra che le funzioni definite nel tratto di tipo superiore Builder perdano alcune informazioni di tipo quando utilizzo il trucco .

C'è un modo per rendere tutto questo perfettamente funzionante? Forse il trucco PartialApplier non è la strada giusta da percorrere!

risposta

4

Nell'esempio in type PartiallyApplied[_] = F[K, _] i caratteri di sottolineatura non specificano lo stesso tipo.

Funziona, se si definisce PartialApplier come

trait PartialApplier[F[_, _], K] { 
    type PartiallyApplied[V] = F[K, V] 
} 

Inoltre, è possibile evitare l'uso di PartialApplier a tutti, e definire Builder tipo di parametro in questo modo:

class BuilderMap[K] extends Builder[({ type L[V] = Map[K, V] })#L] { 
    def build[V] = Map.empty[K, V] 
} 
+0

Questo è un perfetto risposta. Grazie! – jrjd

Problemi correlati