为什么Scala在显而易见的情况下无法推断类型论证?

时间:2015-06-09 07:47:47

标签: scala scalding

在以下示例中,我尝试在MySourceTypedPipe[T]之间创建隐式转换。我拥有MySource,事实上我有很多这样的来源,所以我想使用Porable[T]特征来标记我想要输出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]。为什么不以这种方式推断出来(这会使这个例子有效)?

修改

this question and its answer提到n.m.之后,我修改了我的代码,使用隐式证据参数来表达两个类型参数之间的关系。显式转换调用现在有效,但不是隐式转换调用。所以仍然需要帮助。

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,更不用说可以插入其他特征,如SuccessFileSourceDelimitedScheme。必须为每个基础源/特征组合实现某些东西需要相当多的工作。因此我的特质选择。当然,这不是必须的 - 任何可以使用O(1)实施量的多个基本源/特征组合的答案都可以。

2 个答案:

答案 0 :(得分:3)

鉴于您没有在返回类型中的任何位置使用类型参数S,为什么不让portableSourceToTypedPipe获取Source with Portable[T]。 换句话说:

implicit def portableSourceToTypedPipe[T](source: Source with Portable[T]): TypedPipe[T]

这可以解决您的编译问题。 通常,您越明确,编译器可以解析由类型参数表示的约束的可能性就越高。这首先要完全删除不必要的类型参数。

答案 1 :(得分:1)

您的Source定义表明您可以致电toTypedPipe[T]以获取任何 T。如果您确实希望MySource仅转换为Tuple2[Int, Long],则应为

class TypedSource[T](path: String) extends Source(path) {
  def toSpecificTypedPipe = toTypedPipe[T]
}

class MySource(p: String) extends TypedSource[(Int, Long)](p)

implicit def portableSourceToTypedPipe[T](source: TypedSource[T]): TypedPipe[T] = 
  source.toSpecificTypedPipe

(您也可以使用合成而不是继承TypedSource。)

如果您确实希望能力转换为任何一种“首选”类型,只需摆脱S,您就不需要它了:

implicit def portableSourceToTypedPipe[T](source: Source with Portable[T]): TypedPipe[T]
相关问题