簡體   English   中英

參數化類型的隱式類解析

[英]Implicit class resolution for parameterized types

在下面的示例中,似乎Scala編譯器僅在識別為隱式類時才識別Wrapper的高級表示。 這是為什么?

scala> case class Nested(n: Int)
defined class Nested

scala> case class Wrapper[A <: Product](nested: A)
defined class Wrapper

scala> implicit class I1[W <: Wrapper[A], A <: Product](underlying: W) {
     | def ok1() = true
     | }
defined class I1

scala> Wrapper(Nested(5)).ok1()
<console>:26: error: value ok1 is not a member of Wrapper[Nested]
       Wrapper(Nested(5)).ok1()
                          ^
scala> implicit class I2[W <: Wrapper[_]](underlying: W) {
     | def ok2() = true
     | }
defined class I2

scala> Wrapper(Nested(5)).ok2()
res1: Boolean = true

隱式解決方案是否有一個解決方法來維護有關嵌套類型的完整信息,允許將類型類證據(例如TypeTag附加到它上面?

注意:上面的示例顯示NestedWrapper是案例類,但這不是問題的組成部分。 它只是為更短更簡單的控制台會話提供便利。

由於Scala類型推斷的限制,這種情況正在發生。 SI-2272

隱式無法解析,因為編譯器無法正確推斷A 如果我們啟用-Xlog-implicits我們可以看到這一點。 請注意, A被推斷為Nothing

I1 is not a valid implicit value for Test.w.type => ?{def ok: ?} because:
inferred type arguments [Wrapper[Nested],Nothing] do not conform to method I1's type parameter bounds [W <: Wrapper[A],A <: Product]

如果我們嘗試手動實例化I1會發生同樣的事情:

scala> val w = Wrapper(Nested(5))
w: Wrapper[Nested] = Wrapper(Nested(5))

scala> new I1(w)
<console>:21: error: inferred type arguments [Wrapper[Nested],Nothing] do not conform to class I1's type parameter bounds [W <: Wrapper[A],A <: Product]
       new I1(w)
       ^
<console>:21: error: type mismatch;
 found   : Wrapper[Nested]
 required: W
       new I1(w)
              ^

現在,解決方法。

首先, Wrapper是一個案例類,因此它沒有理由讓它有子類型。 您可以刪除W類型參數,並將underlying更改為Wrapper[A]

implicit class I1[A <: Product](underlying: Wrapper[A]) {
  def ok = true
}

如果您仍然希望需要兩個類型參數,您還可以要求隱式證據W <:< Wrapper[A] ,同時刪除類型參數W的上限:

implicit class I1[W, A <: Product](underlying: W)(implicit ev: W <:< Wrapper[A]) {
  def ok = true
}

邁克爾所說的一切都是真的。 以下是對此問題的一些額外看法。

由於您編寫隱式類的方式,看起來您希望隱式類可以處理Wrapper所有子類型,並且具有盡可能涉及的所有類型的特定信息。 (99%的時候擴展案例類是一個壞主意,但這是可能的,這些技巧也適用於非案例類)。

訣竅主要是確保您想要推斷的所有類型參數都存在於值參數列表中的某處 要記住的另一件事是:

scala> trait Foo[A]; trait Bar extends Foo[Int]
defined trait Foo
defined trait Bar

scala> implicitly[Bar with Foo[Int] =:= Bar]
res0: =:=[Bar with Foo[Int],Bar] = <function1>

拿這兩個知識你可以像這樣重寫你的隱式類:

implicit class I1[Y, A <: Product](underlying: Y with Wrapper[A]) {
  def ok1(): (Y, A) = ???
}

在工作中看到它:

scala> :paste
// Entering paste mode (ctrl-D to finish)

case class Nested(n: Int)
case class Wrapper[A <: Product](nested: A)
class Crazy(override val nested: Nested) extends Wrapper[Nested](nested)

implicit class I1[Y, A <: Product](underlying: Y with Wrapper[A]) {
  def ok1(): (Y, A) = ???
}

// Exiting paste mode, now interpreting.


scala> :type Wrapper(Nested(5)).ok1()
(Wrapper[Nested], Nested)

scala> :type new Crazy(Nested(5)).ok1()
(Crazy, Nested)

請注意,Michael給出的最后一個解決方案基於相同的事情:通過將上限移動到隱式參數列表, A現在存在於值參數列表中,並且可以由編譯器推斷。

暫無
暫無

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

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