I am trying to transform the following HList
Some(C(15)) :: None :: Some(B(55)) :: None :: Some(A(195)) :: HNil
to
C(15) :: B(55) :: A(195) :: HNil
Here is what I have at the moment :
import shapeless._
case class A(value: Int)
case class B(value: Int)
case class C(value: Int)
trait folderLP extends Poly2 {
implicit def default[T, L <: HList] = at[T, L]((acc, t) => acc)
}
object folder extends folderLP {
implicit def none[T, L <: HList] = at[None.type, L]((t, acc) => acc)
implicit def someDiameter[T, L <: HList] = at[Some[C], L]((t, acc) => t.get :: acc)
implicit def someRatio[T, L <: HList] = at[Some[B], L]((t, acc) => t.get :: acc)
implicit def someWidth[T, L <: HList] = at[Some[A], L]((t, acc) => t.get :: acc)
}
val test = Some(C(15)) :: None :: Some(B(55)) :: None :: Some(A(195)) :: HNil
val filtered = test.foldRight[HList](HNil)(folder)
this works but I would like to make this generic so that it works for any type wrapped in Some without having to write each case
First for a literal answer. Note that most of your T
type parameters aren't being used. You can use that T
to make your function match any element of type Some[T]
:
trait folderLP extends Poly2 {
implicit def default[T, L <: HList] = at[T, L]((_, acc) => acc)
}
object folder extends folderLP {
implicit def some[T, L <: HList] = at[Some[T], L]((t, acc) => t.get :: acc)
}
Note that you don't even need the none
case if you switch the order of arguments in your default
.
Also note that you probably want to use the following definition of filtered
:
val filtered = test.foldRight(HNil: HNil)(folder)
This one will have the HNil
statically typed as an HNil
instead of an HList
, which will be useful for pretty much anything you want to do down the line—for example try filtered.length
on your original version and then on this one.
You don't even really need a fold for this operation, though—a flatMap
will do:
trait filterLP extends Poly1 {
implicit def any[T] = at[T](_ => HNil)
}
object filter extends filterLP {
implicit def some[T] = at[Some[T]](_.get :: HNil)
}
And then:
val filtered = test.flatMap(filter)
Finally, it's worth noting that this will only work on an HList
where the None
and Some
elements are statically typed as None
and Some
—a Some[A]
for example that's statically typed as an Option[A]
will get filtered out. This makes it kind of unuseful (at least I can't see a practical use), but there's just not really any way you can perform this kind of type-level filter if you don't know at compile time whether the Option
is empty or not.