带有 ClassTags 的无形 HList

Shapeless HList with ClassTags

我有一个非常简单的函数可以实例化 C 类型的 class:

  def parse[C: ClassTag]: C = implicitly[ClassTag[C]].runtimeClass.getCanonicalName match {
          case "int" ⇒ 10.asInstanceOf[C]
          case "double" ⇒ 10d.asInstanceOf[C]
          case "java.lang.String" ⇒ "qwerty".asInstanceOf[C]
    }

       parse[Int] // res0: Int = 10
       parse[Double] // res1: Double = 10.0
       parse[String] // res2: qwerty = String

我想介绍第二个功能:

def parseAll[T <: HList]: T = ???

并按如下方式使用它:

val defaults: Int :: Double :: String :: HNil = parseAll[Int :: Double :: String :: HNil] 
// res3: shapeless.::[Int,shapeless.::[Double,shapeless.::[String,shapeless.HNil]]] = 10 :: 10.0 :: qwerty :: HNil

有什么想法可以实现这样的功能吗?

你可以定义type class ParseAll:

  import shapeless.{::, HList, HNil}
  import scala.reflect.ClassTag

  def parse[C: ClassTag]: C = implicitly[ClassTag[C]].runtimeClass.getCanonicalName match {
    case "int" ⇒ 10.asInstanceOf[C]
    case "double" ⇒ 10d.asInstanceOf[C]
    case "java.lang.String" ⇒ "qwerty".asInstanceOf[C]
  }

  def parseAll[T <: HList](implicit p: ParseAll[T]): T = p.apply

  trait ParseAll[T <: HList] {
    def apply: T
  }

  object ParseAll {
    implicit def hCons[H: ClassTag, T <: HList](implicit p: ParseAll[T]): ParseAll[H :: T] = new ParseAll[H :: T] {
      override def apply: H :: T = parse[H] :: p.apply
    }

    implicit val hNil: ParseAll[HNil] = new ParseAll[HNil] {
      override def apply: HNil = HNil
    }
  }

  parseAll[Int :: Double :: String :: HNil] // 10 :: 10.0 :: "qwerty" :: HNil

实际上你可以不加思考地做到这一点:

  import shapeless.{::, HList, HNil}

  def parse[C](implicit p: Parse[C]): C = p.apply

  trait Parse[C] {
    def apply: C
  }

  object Parse {
    implicit val int: Parse[Int] = new Parse[Int] {
      override def apply: Int = 10
    }

    implicit val double: Parse[Double] = new Parse[Double] {
      override def apply: Double = 10.0
    }

    implicit val string: Parse[String] = new Parse[String] {
      override def apply: String = "qwerty"
    }
  }

  def parseAll[T <: HList](implicit p: ParseAll[T]): T = p.apply

  trait ParseAll[T <: HList] {
    def apply: T
  }

  object ParseAll {
    implicit def hCons[H, T <: HList](implicit parse: Parse[H], parseAll: ParseAll[T]): ParseAll[H :: T] = new ParseAll[H :: T] {
      override def apply: H :: T = parse.apply :: parseAll.apply
    }

    implicit def hNil: ParseAll[HNil] = new ParseAll[HNil] {
      override val apply: HNil = HNil
    }
  }

  parseAll[Int :: Double :: String :: HNil] // 10 :: 10.0 :: "qwerty" :: HNil