2015-09-01 6 views
6

Ich versuche, etwas Code zu faktorisieren und am Ende mit höher-kinded Typen arbeiten musste. Die folgende minimal Beispiel funktioniert:Typ Informationen verloren gehen, wenn mit hoch-kinded Objekte 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]) 

Wenn ich jetzt dies mit zwei Typargumente auf einen Typ angewendet werden soll, sagen

class Map[K, V] 

Ich möchte in der Lage sein zu schreiben

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

aber das funktioniert natürlich nicht, weil Typ Argumente in Scala nicht curried sind.

fand ich, dass der folgende Trick erlaubte mir Kompilation weitergeben müssen:

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

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

Aber dann einige seltsame Wirkung geschieht, und ich kann den Grund nicht erklären, warum:

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] 

Es scheint, dass die Funktionen, die in dem höher-kinded Merkmal Builder definiert sind, einige Typeninformation verlieren, wenn ich den Trick PartialApplier verwende.

Gibt es eine Möglichkeit, all dies reibungslos zusammen zu arbeiten? Vielleicht ist der PartialApplier Trick nicht der richtige Weg!

Antwort

4

In Ihrem Beispiel in type PartiallyApplied[_] = F[K, _] geben die Unterstriche nicht den gleichen Typ an.

Es funktioniert, wenn Sie PartialApplier als

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

auch definieren, können Sie PartialApplier überhaupt vermeiden, mit und Builder Typ-Parameter wie folgt definieren:

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

Das ist ein perfekt Antworten. Vielen Dank! – jrjd

Verwandte Themen