簡體   English   中英

我如何證明兩種類型在Scala中沒有子類型關系?

[英]How can I prove that two types have no subtyping relation in Scala?

(注意:這樣做的動機需要冗長而困難的解釋;您可以找到有關此Accord問題的完整討論。它甚至可能不是解決該問題的正確方法,但我認為這個問題本身很有趣。)

我正在尋找一種實現二進制運算符的方法,以使行為取決於右側操作數的類型 :如果一個行為與左側操作數相同,則為一個行為,否則為不同行為。 舉個例子:

implicit class Extend[T](lhs: T) {
  def testAgainst(rhs: T) = println("same type")
  def testAgainst[U](rhs: U) = println("different type")
}

第一個重載比第二個重載更具體,因此您希望調用5 testAgainst 10來觸發第一個重載,而5 testAgainst "abcd"將調用第二個重載。 盡管從理論上講這是合理的,但由於兩個重載的已擦除簽名相同,因此無法編譯。

我設法以一種需要在第一次重載中添加類型參數的方式來解決此問題,但這正是我要避免的事情。 另一種解決方案是修改泛型重載,以要求編譯器證明類型之間沒有子類型關系( =:=的相反之處,不幸的是Scala庫未提供)。

雖然在Scala中通常很容易對子類型關系進行編碼,但是我發現沒有辦法對缺少的子類型進行編碼。 有沒有辦法要求第二個重載在編譯時成為候選,那么T <:< UT >:> U都不成立?

如果要在編譯時強制兩種類型嚴格不同,那么這就是的問題 使用定義=!=的答案之一,我們可以想象多個看起來像這樣的方法:

implicit class Extend[T](lhs: T) {
  def testAgainst(rhs: T) = println("same type")
  def testAgainst[U](rhs: U)(implicit ev: T =!= U) = println("different type")
}

我們還可以很容易地使用TypeTag在一種方法中進行類型測試。

import scala.reflect.runtime.universe._

implicit class Extend[T: TypeTag](lhs: T) {
  def testAgainst[U: TypeTag](rhs: U): Boolean = typeOf[T] =:= typeOf[U]
}

然后,您當然可以修改它以分支行為。

scala> 1 testAgainst 2
res98: Boolean = true

scala> 1 testAgainst "a"
res99: Boolean = false

scala> List(1, 2, 3) testAgainst List(true, false)
res100: Boolean = false

scala> List(1, 2) testAgainst List.empty[Int]
res102: Boolean = true

該解決方案實際上非常簡單。 唯一真正的問題是兩個重載都具有相同的擦除,由於底層JVM的限制,這只是編譯器的問題。 就鍵入而言,具有這兩個重載是完全可以的。

因此,您要做的就是以一種功能等效的方式更改一個重載的簽名。 可以使用總是可以找到的隱式參數(通常是標准庫的DummyImplicit )或通過添加具有默認值的虛擬參數來完成此操作。 所以這些都很好(我通常使用第一個版本):

def testAgainst[U](rhs: U)(implicit dummy: DummyImplicit) = println("different type")

要么:

def testAgainst[U](rhs: U, dummy: Int = 0) = println("different type")

還有一種方法可以在運行時確定基於隱式解析規則和默認隱式值的類型之間沒有子類型關系。 這個簡單的函數及其調用可以說明這一點:

scala> def checkSubtypes[T, U](implicit ev: T <:< U = null) = ev
checkSubtypes: [T, U](implicit ev: <:<[T,U])<:<[T,U]

scala> checkSubtypes[Int, Long]
res4: <:<[Int,Long] = null

scala> checkSubtypes[Integer, Number]
res5: <:<[Integer,Number] = <function1>

如果類型T不是其他類型U的子類型,則編譯器將無法找到T <:< U的隱式值,因此將使用默認值,在這種情況下為null

但是,這僅在運行時有效,因此它可能無法准確回答您的問題,但是此技巧有時還是有用的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM