实现某个类型类的类列表

Man*_*idt 8 scala typeclass

我想定义一个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}.

And*_*kin 6

我将首先草拟一个解决方案,然后解释为什么天真的方法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].这个论点非常类似于第三部分"为自由单子定义翻译时处理隐含"这个冗长的答案.