【问题标题】:Features of different kinds of path-dependent types in scalascala中不同类型的路径依赖类型的特征
【发布时间】:2013-06-20 04:21:01
【问题描述】:

假设有一个特质:

trait OuterTrait {
  type InnerType
}

现在我们可以编写非泛型函数someAlgo

def pairToString[S, U](x: S, y: U): String = 
  "{" + y.toString + " in " + x.toString + "}"

def pairPrintln[S, U](x: S, y: U) {
  println(pairToString(x, y))
}

def someAlgo(x: OuterTrait)(y: x.InnerType): x.InnerType = {
  pairPrintln(x, y)
  y
}

以及一系列通用函数:

def someAlgoObjObj[T <: OuterTrait](x: T)(y: x.InnerType): x.InnerType = {
  pairPrintln(x, y)
  y
}

def someAlgoObjType[T <: OuterTrait](x: T)(y: x.InnerType): T#InnerType = {
  pairPrintln(x, y)
  y
}

def someAlgoTypeType[T <: OuterTrait](x: T)(y: T#InnerType): T#InnerType = {
  pairPrintln(x, y)
  y
}

还有一个通用函数无法编译:

def someAlgoTypeObj[T <: OuterTrait](x: T)(y: T#InnerType): x.InnerType = {
  pairPrintln(x, y)
  y
}

似乎: 1)someAlgosomeAlgoObjObj是最正确的函数; 2)在这个例子中使用泛型函数根本没有意义。

我想澄清一下上述通用函数之间的一些区别。如果我犯了错误,请纠正我。

据我了解,T 类型对应于x静态 类型(称为X)或泛型调用的显式类型(例如,我的意思是algo[Int])。这就是为什么T#InnerType 对应于X 类型声明中的类型。但是x.InnerType也对应xstatic类型的InnerType。区别在哪里?

Further... someAlgoObjType 编译,所以看来x.InnerType 必须是T#InnerType子类型。那么someAlgoTypeObj 不编译就可以了,因为我们不能隐式地进行向下转换。虽然我们可以重写最后一个:

def someAlgoTypeObj[T <: OuterTrait](x: T)(y: T#InnerType): x.InnerType = {
  pairPrintln(x, y)
  y.asInstanceOf[x.InnerType]
}

UPD1:如果将它们与显式类型参数一起使用,我发现someAlgoObjObjsomeAlgoTypeType 之间存在一个区别。如果我们写一些扩展OuterTrait的类:

class OuterIntClass extends OuterTrait{
  type InnerType = Int
}
val x: OuterIntClass = new OuterIntClass()
val y: Int = 5

然后:

someAlgoObjObj[OuterTrait](x)(y) // OK

下一个电话不起作用:

someAlgoTypeType[OuterTrait](x)(y)

【问题讨论】:

  • PDT 上有一个很棒的blogpost,你一定要看看它
  • 谢谢,这很有趣。但我对T#InnerType仍有问题...

标签: scala path-dependent-type


【解决方案1】:

T#InnerType 表示“一个 InnerType 属于 一些 T”,而 x.InnerType 表示“一个 InnerType 属于 一个给定的 x(属于 OuterTrait 类型)”。

理解这些的关键是在某些T中在给定的x中。您可以将 in some 解释为 some T 但我们不知道哪个 T 实例,这意味着在两个 T 中不一定相同,因此,T#InnerType 可以'不被证明等于另一个 T#InnerType。

让我们分析签名:

/* 1 */ def someAlgoObjObj[T <: OuterTrait](x: T)(y: x.InnerType): x.InnerType = ???
/* 2 */ def someAlgoObjType[T <: OuterTrait](x: T)(y: x.InnerType): T#InnerType = ???
/* 3 */ def someAlgoTypeType[T <: OuterTrait](x: T)(y: T#InnerType): T#InnerType = ???
  1. 给定 x 和属于 this x 的 InnerType 返回其 InnerType。
  2. 给定 x 和 InnerType 属于 this x 返回属于 some T 的 InnerType,这意味着 some T 不一定与x.
  3. 给定 x 和属于 some T 的 InnerType 返回属于 some T 的 InnerType

现在是第四个:

def someAlgoTypeObj[T <: OuterTrait](x: T)(y: T#InnerType): x.InnerType = y

签名内容为:给定 x 和属于 some T 的 InnerType,返回属于 this x 的 InnerType。但是在实现中,我们尝试返回 y,它属于一个不一定与 x 相同的 T,因此编译器会报错。

【讨论】:

  • 但是 some type T 在整个函数签名上传播。所以我们有 some type Tthis some type T 的参数 x。你说 «Given x and InnerType 属于 this x 返回 InnerType 属于 some T ,这意味着 a some T 不一定与 x 相同。»不一样——可能是,但x 的类型必须是T 的子类型。
  • 我的意思是,当我们调用泛型函数的某个实例时,我们的函数中到处都有相同的实例 QT。而且由于x: Qx.InnerTypeQ#InnerType 不能完全不同。
  • 你没有抓住重点。在(x: T)(y: T#InnerType) 中,此处的 T 表示不同的实例 - 不一定不同,但编译器无法区分。编译器只能依赖您提供的信息,在这种情况下,由于 x.InnerType 和 y 可能不同,它假定它实际上是不同的。
  • 它们在哪些方面会有所不同? def algo[T &lt;: OuterTrait, S &lt;: OuterTrait](x: T)(y: S#InnerType) 也是如此——不同的类型参数。但是类型参数T 在函数的任何地方都是一样的。否则无法编译相同的函数:def id[T](x: T): T = x like def id[T, S](x: T): S = x
【解决方案2】:

关于更新的一点说明。

someAlgoTypeType[OuterTrait](x)(y) 

失败,因为您的方法签名告诉它希望其y 参数符合T#InnerType 类型并且您的y.type 是Int。要使其正常工作,您应该将其类型更改为以下内容:

class OuterIntClass extends OuterTrait{
  type InnerType = Int
}
val x: OuterIntClass = new OuterIntClass()
val y: x.InnerType = 5

现在y 的类型满足类型投影T#InnerTypesomeAlgoTypeType[OuterTrait](x)(y) 编译

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-04-17
    • 1970-01-01
    • 2011-03-20
    • 1970-01-01
    • 1970-01-01
    • 2016-06-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多