Scala : 'implicitly' and type parameter

若如初见. 提交于 2019-12-25 08:54:09

问题


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


回答1:


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).




回答2:


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


来源:https://stackoverflow.com/questions/6077134/scala-implicitly-and-type-parameter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!