我想定义一个List实现公共类型类的元素.例如
trait Show[A] {
def show(a: A): String
}
implicit val intCanShow: Show[Int] = new Show[Int] {
def show(int: Int): String = s"int $int"
}
implicit val stringCanShow: Show[String] = new Show[String] {
def show(str: String): String = str
}
Run Code Online (Sandbox Code Playgroud)
问题是,如何定义一个list = List(1, "abc")这样的值,以确保Show这些值的实例在范围内?然后我想将这个列表映射到show上list map {_.show}.
我将首先草拟一个解决方案,然后解释为什么天真的方法List[Any](1, "abc")无法工作.
你可以做什么
定义一个包装类,它可以包含类型的A实例以及Show[A]以下实例:
case class Showable[A](a: A, showInst: Show[A]) {
def show: String = showInst.show(a)
}
Run Code Online (Sandbox Code Playgroud)
将列表定义为List[Showable[_]]:
var showableList: List[Showable[_]] = Nil
Run Code Online (Sandbox Code Playgroud)
也许定义一个单独的方法来填充这个列表(考虑打包列表本身和类中的builder-method):
def addShowable[A: Show](a: A): Unit = {
showableList ::= Showable[A](a, implicitly[Show[A]])
}
Run Code Online (Sandbox Code Playgroud)
或者,您可以仔细添加(非常严格范围的)隐式转换:
implicit def asShowable[A](a: A)(implicit s: Show[A]): Showable[A] =
Showable(a, s)
Run Code Online (Sandbox Code Playgroud)
然后按如下方式构建列表(请注意显式类型归属):
val showableList = List[Showable[_]](1, "abc")
Run Code Online (Sandbox Code Playgroud)
现在您可以浏览列表并致电show:
showableList.map(_.show)
Run Code Online (Sandbox Code Playgroud)
获得一份清单String.
你不能做什么
你不能简单地定义
val list: List[Any] = List(1, "abc", <showable3>, ..., <showableN>)
Run Code Online (Sandbox Code Playgroud)
然后期望能够打电话show,因为为了打电话Show.show,你需要实际的Show实例.这些东西不是可以在运行时擦除的某些类型提示,它们是实际对象,它们必须由编译器提供.一旦你创建了一个List[Any],就会丢失所有类型,因为所有类型都被合并到一个未表达的上限Any,并且编译器无法注入所有必要的含义Show[T_1],...,Show[T_N].这个论点非常类似于第三部分"为自由单子定义翻译时处理隐含"这个冗长的答案.