[英]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.