The easiest way is to use a fold. First we need a polymorphic function that will add each item to the accumulator if it has the desired type (String => A
for some A
), and ignore it otherwise:
trait ignore extends Poly2 {
implicit def default[A, L <: HList] = at[A, L]((_, l) => l)
}
object keepStringFunc extends ignore {
implicit def stringFunc[A, L <: HList] = at[String => A, L](_ :: _)
}
Now the following will give the result you want in both 1.2.4 and 2.0.0-M1:
val filtered = hflist.foldRight(HNil)(keepStringFunc)
You could also write your own type class on the model of Filter
, FilterAux
(or Filter.Aux
), etc.—and doing so would be a good exercise if you're trying to get the hang of Shapeless—but foldRight
is a lot simpler.
Update: actually, for what it's worth, there's a slightly more concise way to do this with flatMap
:
trait skip extends Poly1 {
implicit def default[A] = at[A](_ => HNil)
}
object grabStringFunc extends skip {
implicit def stringFunc[A] = at[String => A](_ :: HNil)
}
val filtered = hflist flatMap grabStringFunc
I personally find the foldRight
version a little more obvious, but this one's also pretty elegant.
In response to your comment: you can make the solution a little more generic like this:
trait skip extends Poly1 {
implicit def default[A] = at[A](_ => HNil)
}
trait grabFuncFrom[T] extends skip {
implicit def stringFunc[A] = at[T => A](_ :: HNil)
}
object grabStringFunc extends grabFuncFrom[String]
val filtered = hflist flatMap grabStringFunc
But you're still going to need that last step where you create the higher rank function as an object (see e.g. this answer and Miles's comment there for some discussion of this issue).