Scala 参数化类型的隐式类解析

Scala 参数化类型的隐式类解析,scala,higher-kinded-types,implicits,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 <: Produ

在下面的示例中,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
scala>case类嵌套(n:Int)
定义类嵌套
scala>case类包装器[A隐式类I1[W隐式类I2[W包装器(嵌套的(5)).ok2()
res1:Boolean=true
是否有隐式解析的解决方案,可以维护嵌套类型的完整信息,允许将类型类证据(例如,
TypeTag
)附加到嵌套类型


注意:上面的示例显示
嵌套的
包装器
是案例类,但这不是问题的一部分。这只是为了方便进行更短更简单的控制台会话。

这是因为Scala的类型推断受到限制。请参阅

隐式解析失败,因为编译器无法正确推断
A
。如果启用
-Xlog implicits
,我们可以看到这一点。请注意,
A
被推断为

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]
现在,工作开始了

首先,
Wrapper
是一个case类,因此它不应该有子类型的理由。您可以删除
W
类型参数,并将
基础
更改为
Wrapper[a]

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

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

由于您编写隐式类的方式,您似乎希望隐式类能够处理
包装器的所有子类型,并尽可能提供有关所有类型的特定信息(99%的情况下,扩展case类是个坏主意,但这是可能的,这些技巧也适用于非case类)

技巧基本上是确保所有要推断的类型参数都存在于值参数列表中的某个位置

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>
scala>trait Foo[A];trait Bar扩展Foo[Int]
定义特征Foo
定义特征条
scala>隐式[Bar with Foo[Int]=:=Bar]
res0:=:=[带Foo的条形图[Int],条形图]=
利用这两项知识,您可以像这样重写隐式类:

implicit class I1[Y, A <: Product](underlying: Y with Wrapper[A]) {
  def ok1(): (Y, A) = ???
}
隐式类I1[Y,A:粘贴
//进入粘贴模式(按ctrl-D键完成)
案例类嵌套(n:Int)
案例类包装器[A:键入new(嵌套的(5)).ok1()
(疯狂的,嵌套的)

请注意,Michael给出的最后一个解决方案是基于相同的东西:通过将上限移动到隐式参数列表,
A
现在出现在值参数列表中,并且可以由编译器推断出来。

I1
对我没有多大帮助。关于
A@cchantep,已经有一个类型约束了,你还能做什么您是否知道
A
的详细信息,或者将
类型标签
与之关联?附加上下文非常有用。
-Xlog implicits
是一个很好的提示。
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)