简体   繁体   English

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

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

I'm trying to model Table-like collection, with strongly typed row data access. 我正在尝试对具有强类型行数据访问权限的表式集合进行建模。 I'm using F-bounded polymorphism (recursive type) pattern in order to carry Table type information through transformations (eg to have access to column list in DataView being result of table filtering). 我正在使用F界多态性(递归类型)模式,以便通过转换来携带表类型信息(例如,通过表过滤的结果访问DataView中的列列表)。 Everything works fine, as long as actual type is used. 只要使用实际类型,一切都可以正常工作。 Please take a look at snippet below for problematic common supertype operation. 请查看下面的代码片段,了解有问题的普通超型操作。

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...

Last line produces compiler error: 最后一行产生编译器错误:

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]

It seems self type bounds are not preserved in type being alternative of Table1 and Table2. 似乎不能保留自身类型的边界,而不能作为表1和表2的替代类型。 Is there any known workaround for that? 有任何已知的解决方法吗?

Update: Correct me if I'm wrong, but I think I've wrongly assumed that Table1 and Table2 have common supertype, which has characterictics of DataTable. 更新:如果我错了,请纠正我,但是我认为我错误地认为Table1和Table2具有共同的超类型,它具有DataTable的特征。 They have common supertype of DataTable[_], but this is no longer valid DataTable - and this is exactly what scala compiler is trying to tell me :). 它们具有DataTable [_]的通用超类型,但这不再是有效的DataTable-而这正是Scala编译器试图告诉我的:)。

Indeed it might be possible to try with existential types, but introducing GenericDataTable type as a base for DataTable[A] would solve problem in much more straightforward way. 确实有可能尝试使用存在性类型,但是将GenericDataTable类型引入为DataTable [A]的基础将以更直接的方式解决问题。

In my case - this is unfortunately not that easy - as it requires to build another compliment hierarchy of interconnected classes. 以我为例-不幸的是,这并不容易-因为它需要建立互连类的另一个称赞层次。

Marcin 马尔钦

I learned a few thnigs myself trying to figure out your question: 我自己学会了一些想法,试图弄清您的问题:

The return type for getTable is: getTable的返回类型为:

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

Which practically is DataTable[_] 实际上是DataTable[_]

The below code also fails: 以下代码也失败:

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

It makes a lot of sense since dump is expecting some specific subtype of DataTable , and it gets its abstract type, DataTable[_] . 这很有意义,因为dump期望使用DataTable某些特定子类型,并且获得其抽象类型DataTable[_]

So, if the concrete subtype is hidden behind its abstract supertype, I doubt that F-bounded would make any sense: If the input type is DataTable[_] , I would be surprised if you could infer the specific subtype of the object behind it statically. 因此,如果具体的子类型隐藏在其抽象超类型的后面,则我怀疑F边界是否有意义:如果输入类型为DataTable[_] ,那么如果您推断出其后面的对象的特定子类型,我会感到惊讶静态。

Since getTables can return more that one type of DataTable , its return type probably cannot be more specific than DataTable[_] . 由于getTables可以返回一种以上类型的DataTable ,因此其返回类型可能不能比DataTable[_]更具体。 Indeed working on this is the most promising path, but the suggested solution in the comment is not compiling: 确实,这是最有前途的途径,但是注释中建议的解决方案尚未编译:

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

EDIT: I made it work! 编辑:我做到了! And it is weird!: 而且很奇怪!:

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"))

The trick was to force at least one of the returned tables to be of type A forSome {type A <: DataTable[A] ! 诀窍是强制至少一个返回的表的类型A forSome {type A <: DataTable[A] So, a factory of various subtype objects can expose the concrete type of the returned object! 因此,具有各种子类型对象的工厂可以公开返回对象的具体类型! Mind blown, I have to say. 我要说的很震惊。


I have to point out that dump could just be: 我必须指出,转储可能只是:

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

It is when the input type is also in the return type that we care using F-bounded types in the first place, for example: 当输入类型也属于返回类型时,我们首先要使用F绑定类型,例如:

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

Since this statically stops us from getting a Table1 and returning a Table2. 由于这会静态阻止我们获取Table1并返回Table2。

All in all, F-bounded methods (which are the motivation for creating F-bound types) make sense when the type of the input is a concrete subtype of the F-bounded type, and the type is used in the return type, directly as A or indirectly, as Something[A] . 总而言之, 当输入的类型是 F绑定类型的具体子类型,并且该类型直接用于返回类型时 ,F绑定方法 (创建F绑定类型的动机)才有意义。作为A或间接地作为Something[A]

That is a scala version of catch-22. 那是catch-22的Scala版本。

You have omitted the return type, scala silently miscalculates it and gives your unrelated error. 您省略了返回类型,scala默默地计算了返回类型并给出了不相关的错误。

The root of all evil: DataTable[_] is not a correct existential type for F-bounded type defined as DataTable[A <: DataTable[A]] It is a shorthand for DataTable[A] forSome {type A <: Any} F-bound was lost here. 万恶之源: 对于定义为DataTable[A <: DataTable[A]] F边界类型, DataTable[_]不是正确的存在类型,它是DataTable[A] forSome {type A <: Any}的简写DataTable[A] forSome {type A <: Any} F绑定在这里丢失了。 The correct type is DataTable[A] forSome { type A <: DataTable[A] } 正确的类型是DataTable[A] forSome { type A <: DataTable[A] }

Scala's misbehaviour: when in doubt the type checker infers type to something like DataTable[_] . Scala的不当行为:如果有疑问,类型检查器会将类型推断为DataTable[_]类的东西。 Well, it forges little more precision, but still is loosing F-bound . 嗯,它提高了精度,但是仍然失去了F约束

So the answer is to define correct type and specify it explicitly: 因此,答案是定义正确的类型并明确指定它:

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