首页 > 解决方案 > Scala:递归模式匹配异构列表,获取每个元素的正确类型

问题描述

我正在尝试做类似下面的事情,即HList通过模式匹配头部和尾部来递归处理,每次将头部传递给通用函数。

import shapeless._
trait MyTrait {

  def myFunc[T](x: String => T): Boolean

  def iter(myHList: HList, acc: List[Boolean]): List[Boolean] = {
    myHList match {
      case HNil => acc
      case head :: tail => myFunc(head) :: iter(tail, acc)
    }
  }
}

问题是我从匹配中得到的头部是类型Any而不是我放入HList. 我希望将 head 作为参数的函数具有正确的类型参数T

这可能吗?也许除了Shapeless之外还有其他方法?

标签: scalagenericsgeneric-programmingshapeless

解决方案


尝试

def iter(myHList: HList, acc: List[Boolean]): List[Boolean] = {
  (myHList: @unchecked) match {
    case HNil => acc
    case (head: Function1[String, _] @unchecked) :: tail => 
      myFunc(head) :: iter(tail, acc)
  }
}

(第一个@unchecked禁止警告模式匹配并不详尽,第二个@unchecked禁止警告关于未检查的泛型String,因为类型擦除)。

或者,您可以更安全地匹配类型。但通常写一个论点只是一个HList而不是具体A :: B :: ... :: HNil的太粗糙了。由于您的函数对不同类型的值(即HNilH :: T)的作用不同,因此它是Poly.

object iter extends Poly2 {
  implicit val nilCase: Case.Aux[HNil, List[Boolean], List[Boolean]] = 
    at((_, acc) => acc)

  implicit def consCase[A, T <: HList](implicit 
    tailCase: Case.Aux[T, List[Boolean], List[Boolean]]
    ): Case.Aux[(String => A) :: T, List[Boolean], List[Boolean]] =
    at { case (head :: tail, acc) => myFunc(head) :: iter(tail, acc) }
}

用法:

def myFunc[T](x: String => T): Boolean = true

iter(((s: String) => s.toUpperCase) :: ((s: String) => s.length) :: HNil, List[Boolean]()) 
// List(true, true)

推荐阅读