1

I am trying to do something like below, that is, process an HList recursively by pattern matching the head & tail, each time passing the head to a generic function.

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)
    }
  }
}

The problem is that the head that I get from matching is of type Any rather than the type of what I put into the HList. I want the function that takes head as an argument to have the correct type parameter T.

Is this possible? Maybe with some other means besides Shapeless?

Adam Mackler
  • 1,980
  • 1
  • 18
  • 32
  • https://stackoverflow.com/questions/34115045/how-to-pattern-match-head-and-tail-types-of-a-scala-list – Shankar Shastri Jul 03 '18 at 04:22
  • Possible duplicate of [How to pattern match head and tail types of a scala list?](https://stackoverflow.com/questions/34115045/how-to-pattern-match-head-and-tail-types-of-a-scala-list) – Shankar Shastri Jul 03 '18 at 04:22
  • Possible duplicate of [Can Map be performed on a Scala HList](https://stackoverflow.com/questions/5349482/can-map-be-performed-on-a-scala-hlist) – Harald Gliebe Jul 03 '18 at 04:42

1 Answers1

0

Try

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)
  }
}

(the first @unchecked suppresses warning that pattern matching is not exhaustive, the second @unchecked suppresses warning about unchecked generic String because of type erasure).

Alternatively you can match more type-safely. But normally writing that an argument is just an HList rather than specific A :: B :: ... :: HNil is too rough. Since your function acts differently on values of different types (namely, HNil and H :: T) it's a 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) }
}

Usage:

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

iter(((s: String) => s.toUpperCase) :: ((s: String) => s.length) :: HNil, List[Boolean]()) 
// List(true, true)
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66