In the following example, I was trying to create an implicit conversion between MySource
and TypedPipe[T]
. I own MySource
, in fact I have a lot of such sources, so I wanted to use a Porable[T]
trait to mark what type argument T
I want for the output TypedPipe[T]
, so that the implicit conversion can automatically do the .toTypedPipe[T]
part (so that I don't have to write .toTypedPipe[T]
for every source I have when I use them).
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
But the problem is, Scala seem to not be able to infer T
for the last statement:
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>
From the example, it's pretty obvious that MySource
implements Portable[Tuple2[Int, Long]]
, so T
should be Tuple2[Int, Long]
. Why it's not inferred this way (which would have made the example work)?
EDIT:
Following this question and its answer mentioned by n.m., I revised my code to use an implicit evidence parameter to express the relation between the two type arguments. The explicit conversion call works now, but not the implicit conversion call. So still need help.
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
The reason for me to choose a trait Portable[T]
is that it can potentially work with multiple base Source
types. One familiar with Scalding might know that we have many types of sources, e.g. DailySuffixSource, HourlySuffixSource, not to mention that one can plug-in other traits like SuccessFileSource
and DelimitedScheme
. Having to implement something for each base source/traits combination will need quite a bit of work. Thus my trait choice. Of course it's not a must - any answer that can with multiple base source/traits combinations with O(1) amount of implementation will do.
Given that you are not using the type parameter S anywhere in the return type, why not just have
portableSourceToTypedPipe
take aSource with Portable[T]
. In other words:This trivially fixes your compilation problem. In general, the more explicit you are, the higher the chances are that the compiler can resolve the constraints represented by the type parameters. This starts with removing unnecessary type parameters altogether.