繁体   English   中英

F界多态性-普通超型不遵循类型界限

[英]F-bounded polymorphism - common supertype doesn't follow type bounds

我正在尝试对具有强类型行数据访问权限的表式集合进行建模。 我正在使用F界多态性(递归类型)模式,以便通过转换来携带表类型信息(例如,通过表过滤的结果访问DataView中的列列表)。 只要使用实际类型,一切都可以正常工作。 请查看下面的代码片段,了解有问题的普通超型操作。

trait DataTable[A <: DataTable[A]] { self: A =>
  def table: A = self
  def name: String
}

class Table1 extends DataTable[Table1] {
  val name = "Table1"
}

class Table2 extends DataTable[Table2] {
  val name = "Table2"
}

def dump[A <: DataTable[A]](table: A) = println(table.name)

def getTable(name: String) = name match {
   case "Table1" => new Table1
   case "Table2" => new Table2
}

dump(new Table1())
dump(getTable("Table1") // doesn't typecheck...

最后一行产生编译器错误:

inferred type arguments [DataTable[_2]] do not conform to method dump's type parameter bounds [A <: DataTable[A]]
found: DataTable[_2] where type _2 >: Table1 with Table2 <: DataTable[_ >: Table1 with Table2 <: Object]

似乎不能保留自身类型的边界,而不能作为表1和表2的替代类型。 有任何已知的解决方法吗?

更新:如果我错了,请纠正我,但是我认为我错误地认为Table1和Table2具有共同的超类型,它具有DataTable的特征。 它们具有DataTable [_]的通用超类型,但这不再是有效的DataTable-而这正是Scala编译器试图告诉我的:)。

确实有可能尝试使用存在性类型,但是将GenericDataTable类型引入为DataTable [A]的基础将以更直接的方式解决问题。

以我为例-不幸的是,这并不容易-因为它需要建立互连类的另一个称赞层次。

马尔钦

我自己学会了一些想法,试图弄清您的问题:

getTable的返回类型为:

def getTable(name: String): DataTable[_ >: Table1 with Table2 <: DataTable[_ >: Table1 with Table2]]

实际上是DataTable[_]

以下代码也失败:

val tbl: DataTable[_] = new Table1
dump(tbl)

这很有意义,因为dump期望使用DataTable某些特定子类型,并且获得其抽象类型DataTable[_]

因此,如果具体的子类型隐藏在其抽象超类型的后面,则我怀疑F边界是否有意义:如果输入类型为DataTable[_] ,那么如果您推断出其后面的对象的特定子类型,我会感到惊讶静态。

由于getTables可以返回一种以上类型的DataTable ,因此其返回类型可能不能比DataTable[_]更具体。 确实,这是最有前途的途径,但是注释中建议的解决方案尚未编译:

def getTable(name: String): A forSome { type A <: DataTable[A] }

编辑:我做到了! 而且很奇怪!:

type DataTableSubtype = A forSome {type A <: DataTable[A]}
def convertToSubtype(tbl:DataTableSubtype): DataTableSubtype = tbl
def getTable(name: String): DataTableSubtype = {
  name match {
    case "Table1" => {convertToSubtype(new Table1)}
    case "Table2" => {(new Table2())}
  }
}

dump(getTable("Table1"))

诀窍是强制至少一个返回的表的类型A forSome {type A <: DataTable[A] 因此,具有各种子类型对象的工厂可以公开返回对象的具体类型! 我要说的很震惊。


我必须指出,转储可能只是:

def dump(table: DataTable[_]): Unit

当输入类型也属于返回类型时,我们首先要使用F绑定类型,例如:

def id[A <: DataTable[A]](table: A): A = table

由于这会静态阻止我们获取Table1并返回Table2。

总而言之, 当输入的类型是 F绑定类型的具体子类型,并且该类型直接用于返回类型时 ,F绑定方法 (创建F绑定类型的动机)才有意义。作为A或间接地作为Something[A]

那是catch-22的Scala版本。

您省略了返回类型,scala默默地计算了返回类型并给出了不相关的错误。

万恶之源: 对于定义为DataTable[A <: DataTable[A]] F边界类型, DataTable[_]不是正确的存在类型,它是DataTable[A] forSome {type A <: Any}的简写DataTable[A] forSome {type A <: Any} F绑定在这里丢失了。 正确的类型是DataTable[A] forSome { type A <: DataTable[A] }

Scala的不当行为:如果有疑问,类型检查器会将类型推断为DataTable[_]类的东西。 嗯,它提高了精度,但是仍然失去了F约束

因此,答案是定义正确的类型并明确指定它:

object DataTable {
  type Any = DataTable[A] forSome {type A <: DataTable[A]}
}

def getTable(name: String) : DataTable.Any = // fill here

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM