簡體   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