参数化类型的隐式类解析

时间:2017-02-13 23:28:32

标签: scala higher-kinded-types implicits

在下面的示例中,似乎Scala编译器仅在识别出隐式类时才识别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

是否存在隐式解决方案的解决方法,该解决方案维护有关嵌套类型的完整信息,允许将类型类证据(例如Nested)附加到其上?

注意:上面的示例将Wrappervar yourObject = { "Order Number":102541029, "Tracking Number":192048236154915, "Secondary Tracking":87350125235, "Items":[{ "SKU":"0200-02-01-NP-P-00", "QTY":4 }, { "SKU":"0120-02-01-XP-T-00", "QTY":2 }] }; var items = yourObject.Items; for (var i = 0; i < items.length; i ++){ console.log(items[i].QTY); }显示为案例类,但这不是问题的组成部分。它只是为更简单,更简单的控制台会话提供便利。

2 个答案:

答案 0 :(得分:4)

由于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
}

答案 1 :(得分:1)

迈克尔所说的一切都是真的。以下是关于这个问题的一些额外观点。

由于您编写隐式类的方式,看起来您希望隐式类适用于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现在存在于值参数列表中,并且可以由编译器推断。 / p>