Reputation: 2557
I'm having a little trouble understanding the following phenomenon:
trait Trait[A] {
def traitType: String
}
object Trait {
implicit val stringTrait: Trait[String] = new Trait[String] {
def traitType: String = "string"
}
implicit val intTrait: Trait[Int] = new Trait[Int] {
def traitType: String = "int"
}
}
class Media[A] {
// works
def mediaType(implicit t: Trait[A]): String = t.traitType
// does not compile
def mediaType: String = implicitly[Trait[A]].traitType
}
object Main {
def main(args: Array[String]) {
val a = new Media[String]
val b = new Media[Int]
println(a.mediaType)
println(b.mediaType)
}
}
In the above snippet I show 2 different implementations of the mediaType method (I comment one of them out when compiling the code). However the version using implicitly does not compile? I get the following error message:
impl.scala:19: error: could not find implicit value for parameter e: Trait[A]
def mediaType: String = implicitly[Trait[A]].traitType
^
one error found
I do understand that there is no implicit value of Trait[A] available. I don't understand why A does not get resolved to the type Media gets instantiated with. I think I'm thinking too much in terms of C++ templates here and I would be very grateful if someone could give me a pointer into the right direction.
Regards, raichoo
Upvotes: 0
Views: 6486
Reputation: 26566
Compiler needs evidence, that there exist implicit Trait
instance for the A
. In first mediaType
implementation you declare this requirement. But in the second implementation, from the compiler's point of view, there is no such guarantee. So in order to make it work you should ask users of Media
class to provide it. You can make this with context bound:
class Media[A : Trait] {
def mediaType: String = implicitly[Trait[A]].traitType
}
This can also be written more explicitly:
class Media[A](implicit val evidence: Trait[A]) {
def mediaType: String = implicitly[Trait[A]].traitType
}
So in other words default constructor requires implicit evidence
and users would not be able to instantiate Media
class without providing it (explicitly or implicitly).
Upvotes: 10
Reputation: 59994
If you want this version to compile:
def mediaType: String = implicitly[Trait[A]].traitType
then the implicit instance of Trait[A]
needs to be passed along, e.g. when a new instance of Media
is created. Try defining Media
as follows instead:
class Media[A](implicit private val t: Trait[A]) {
def mediaType: String = t.traitType
}
An almost equivalent definition using a context bound is this one:
class Media[A: Trait] {
def mediaType: String = implicitly[Trait[A]].traitType
}
That being said, if what you're trying to do is keep more information about the type parameter of a parametrized type, you may want to use Manifest
s instead of your own mechanism. They would provide you with full type information on A
at runtime, including if A
is itself a parametrized type:
scala> class Media[A](implicit val aManifest: Manifest[A])
defined class Media
scala> new Media[Int].aManifest
res0: Manifest[Int] = Int
scala> new Media[Seq[(Int, String)]].aManifest
res1: Manifest[Seq[(Int, String)]] = scala.collection.Seq[scala.Tuple2[Int, java.lang.String]]
Upvotes: 2