raichoo
raichoo

Reputation: 2557

Scala : 'implicitly' and type parameter

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

Answers (2)

tenshi
tenshi

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

Jean-Philippe Pellet
Jean-Philippe Pellet

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 Manifests 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

Related Questions