在Scala中处理高级对象时,类型信息会丢失

jrj*_*rjd 6 scala type-inference higher-kinded-types

我试图分解一些代码,最终不得不使用更高级的类型.以下最小示例正常工作:

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] = List@5c7754a7
ll: (List[String], List[Double]) = (List@62874648,List@7b0f6537)
Run Code Online (Sandbox Code Playgroud)

如果我现在想将它应用于具有两个类型参数的类型,比如说

class Map[K, V]
Run Code Online (Sandbox Code Playgroud)

我想能写

trait BuilderMap[K] extends Builder[Map[K, _]] {...}
Run Code Online (Sandbox Code Playgroud)

但当然这不起作用,因为Scala中的类型参数不是咖喱.

我发现以下技巧允许我通过编译:

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

class BuilderMap[K] extends Builder[PartialApplier[Map, K]#PartiallyApplied] {
    def build[V] = new Map[K, V]
}
Run Code Online (Sandbox Code Playgroud)

但后来发生了一些奇怪的效果,我无法弄清楚原因:

scala> val m: Map[Int, String] = (new BuilderMap[Int]).build[String]
m: Map[Int,String] = Map@71da0c64

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]
Run Code Online (Sandbox Code Playgroud)

Builder当我使用这个PartialApplier技巧时,似乎在高级特性中定义的函数会丢失一些类型信息.

有没有办法让所有这些工作顺利进行?也许PartialApplier诀窍不是正确的方法!

Kol*_*mar 4

在您的示例中,type PartiallyApplied[_] = F[K, _]下划线不指定相同的类型。

它有效,如果你定义PartialApplier

trait PartialApplier[F[_, _], K] {
  type PartiallyApplied[V] = F[K, V]
}
Run Code Online (Sandbox Code Playgroud)

PartialApplier另外,您可以完全避免使用,并Builder像这样定义类型参数:

class BuilderMap[K] extends Builder[({ type L[V] = Map[K, V] })#L] {
  def build[V] = Map.empty[K, V]
}
Run Code Online (Sandbox Code Playgroud)