ant
ant

Reputation: 13

Shapeless HList with ClassTags

I have a quite simple function that is able to instantiate the class of type C:

  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

I'd like to introduce second function:

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

And use it as follows:

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

Any ideas how can I implement such function?

Upvotes: 1

Views: 152

Answers (1)

Dmytro Mitin
Dmytro Mitin

Reputation: 51703

You can define 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

Actually you can do this without reflection:

  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

Upvotes: 0

Related Questions