Scala:遍历类型类对象

时间:2018-12-09 17:19:13

标签: scala shapeless

我在Scala程序中使用了几种外部数据类型。我的目标是使用类型类在这些数据类型上定义多态函数。问题在于,在Scala程序本身中,必须在某些点上依次迭代这些数据类型,这些点与我需要使用类型类中的多态函数的点相同。例如,如果我们有

trait Show[A] {
  def show(a: A, b : String): String
}

object Show {

  def apply[A](implicit sh: Show[A]): Show[A] = sh

  def show[A: Show](a: A, b : String) = Show[A].show(a, b)

  implicit class ShowOps[A: Show](a: A) {
    def show(b : String) = Show[A].show(a, b)}


  implicit val blogCanShow: Show[Int] = new Show[Blog] {
    def show(blog: Blog, b : String): String = s"blog $blog" + b
}
  implicit val twitterCanShow: Show[String] = new Show[Twitter] {
    def show(twitter: Twitter, b : String): String = s"twitter $twitter" + b    }
}

然后我需要使用如下数据类型:

for each data type:
  call show()
  business logic
  call another polymorphic function like show()
  more business logic
  etc... 

我尝试使用Shapeless中的HLists,但无法完全弄清楚如何获得可重复使用的代码块。我想我需要在这里进行一些反思,但不知道从哪里开始。任何建议或帮助将不胜感激。

1 个答案:

答案 0 :(得分:2)

有关此问题的著名讨论,请参见http://tpolecat.github.io/2015/04/29/f-bounds.html

最重要的是(您希望看到该帖子的底部)您想要类似于Seq[(A,Show[A]) forSome {type A}]的内容,因此您可以同时访问A及其Show。没有运行时反射就无需存储Show[A]了,但是Rob展示了一个更优美的技巧来包含该对:

trait ∃[F[_]] {
  type A
  val a: A
  val fa: F[A]
}
object ∃ {
  def apply[F[_], A0](a0: A0)(implicit ev: F[A0]): ∃[F] =
    new ∃[F] {
      type A = A0
      val a = a0
      val fa = ev
    }
}

所以你可以声明

val shows: List[∃[Show]] = ∃(myBlog) :: ∃(myTweet) :: Nil

并对其进行迭代,根据需要引用afa

对我来说,这比HList解决方案更可取,因为尽管代码最初看起来有点不透明,但是可以通过在IDE中按住Ctrl并单击鼠标右键迅速消除任何将来读者的困惑。