【发布时间】:2015-06-09 16:54:28
【问题描述】:
在以下示例中,我试图在 MySource 和 TypedPipe[T] 之间创建隐式转换。我拥有MySource,其实我有很多这样的来源,所以我想使用Porable[T] trait 来标记我想要输出TypedPipe[T] 的类型参数TypedPipe[T],以便隐式转换可以自动做.toTypedPipe[T] 部分(这样我在使用它们时不必为我拥有的每个来源写.toTypedPipe[T])。
import scala.language.implicitConversions
// The following two are pre-defined and I cannot change them
class TypedPipe[T](val path: String) {
def mapWithValue = {
println("values from " + path + " of type " + this.getClass)
}
}
class Source(val path: String) {
def toTypedPipe[T] = { new TypedPipe[T](path) }
}
// The following are defined by me, so yes I can change them.
trait Portable[T]
class MySource(p: String) extends Source(p) with Portable[Tuple2[Int, Long]]
object conversions {
implicit def portableSourceToTypedPipe[S <: Source with Portable[T], T](source: S): TypedPipe[T] = {
source
.toTypedPipe[T]
}
}
import conversions._
portableSourceToTypedPipe(new MySource("real_path")).mapWithValue
但问题是,Scala 似乎无法推断出最后一条语句的T:
scala> import scala.language.implicitConversions
import scala.language.implicitConversions
scala> class TypedPipe[T](val path: String) {
| def mapWithValue = {
| println("values from " + path + " of type " + this.getClass)
| }
| }
defined class TypedPipe
scala> class Source(val path: String) {
| def toTypedPipe[T] = { new TypedPipe[T](path) }
| }
defined class Source
scala>
scala> trait Portable[T]
defined trait Portable
scala>
scala> class MySource(p: String) extends Source(p) with Portable[Tuple2[Int, Long]]
defined class MySource
scala> object conversions {
| implicit def portableSourceToTypedPipe[S <: Source with Portable[T], T](source: S): TypedPipe[T] = {
| source
| .toTypedPipe[T]
| }
| }
defined module conversions
scala> import conversions._
import conversions._
scala> portableSourceToTypedPipe(new MySource("real_path")).mapWithValue
<console>:17: error: inferred type arguments [MySource,Nothing] do not conform to method portableSourceToTypedPipe's type parameter bounds [S <: Source with Portable[T],T]
portableSourceToTypedPipe(new MySource("real_path")).mapWithValue
^
<console>:17: error: type mismatch;
found : MySource
required: S
portableSourceToTypedPipe(new MySource("real_path")).mapWithValue
^
scala>
从示例中,很明显MySource 实现了Portable[Tuple2[Int, Long]],所以T 应该是Tuple2[Int, Long]。为什么不以这种方式推断(这会使示例工作)?
编辑:
在n.m. 提到的this question and its answer 之后,我修改了我的代码以使用隐式证据参数来表达两个类型参数之间的关系。显式转换调用现在有效,但隐式转换调用无效。所以还是需要帮助。
scala> object conversions {
| implicit def portableSourceToTypedPipe[S, T](source: S)(implicit ev: S <:< Source with Portable[T]): TypedPipe[T] = {
| source
| .toTypedPipe[T]
| }
| }
defined module conversions
scala> import conversions._
import conversions._
scala> portableSourceToTypedPipe(new MySource("real_path")).mapWithValue
values from real_path of type class $line4.$read$$iw$$iw$TypedPipe
scala> (new MySource("real_path")).mapWithValue
<console>:17: error: Cannot prove that MySource <:< Source with Portable[T].
(new MySource("real_path")).mapWithValue
^
<console>:17: error: value mapWithValue is not a member of MySource
(new MySource("real_path")).mapWithValue
EDIT2
我选择一个特征Portable[T] 的原因是它可能适用于多个基本Source 类型。熟悉 Scalding 的人可能知道我们有多种来源,例如DailySuffixSource、HourlySuffixSource,更不用说可以插入其他特征,如 SuccessFileSource 和 DelimitedScheme。必须为每个基本源/特征组合实现一些东西需要相当多的工作。因此我的特质选择。当然这不是必须的 - 任何可以使用多个基本源/特征组合和 O(1) 实现量的答案都可以。
【问题讨论】:
-
感谢您的指出。使用隐式证据后,显式转换调用(即最后一条语句)起作用。但是隐式转换仍然不起作用。将编辑问题以添加这些。
-
鉴于您没有在返回类型中的任何地方使用类型参数
S,为什么不让portableSourceToTypedPipe使用Source with Portable[T](如implicit def portableSourceToTypedPipe[T](source: Source with Portable[T]): TypedPipe[T])?这可以轻松解决您的编译问题。 -
@Régis:啊!它起到了魅力的作用!所以我想这里的要点是,如果一个类型参数没有在代码的任何地方显式使用,我们应该尝试将它从参数列表中删除。
-
顺便说一句,你知道为什么这不起作用吗?
implicit def portableSourceToTypedPipe[S, T](source: S)(implicit ev: S <:< Source with Portable[T]): TypedPipe[T] = { ... }