How do I transform an HList into an HList of HLists as in the following snippet.
import shapeless._
import Nat._
case class A(i: Int)
case class B(str: String)
case class C(i: Int, str: String)
type Input = A :: B :: C :: HNil
val in: Input = A(1) :: B("b") :: C(2, "c") :: HNil
type X = A :: HNil
val x: X = A(1) :: HNil
type Y = A :: B :: HNil // could also be B :: HNil
val y: Y = A(1) :: B("b") :: HNil
type Z = A :: C :: HNil // could also be B :: C :: HNil
val z: Z = A(1) :: C(2, "c") :: HNil
type Output = X :: Y :: Z :: HNil
val out: Output = x :: y :: z :: HNil
// Illustrates what I want to accomplish.
def build(in: Input) : Output = {
val x: X = in(_0) :: HNil
val y: Y = in(_0) :: in(_1) :: HNil
val z: Z = in(_0) :: in(_2) :: HNil
x :: y :: z :: HNil
}
println(build(in) == out) // true
def magic[In <: HList, Out <: HList](in: In) : Out = ???
println(magic[Input, Output](in) == out)
I want to build Output
given Input
by way of the magic
method that somehow maps over the input and ends up with what build
outputs.
This isn't too bad with a custom type class. Note that in a sense we need two "base cases"—one to kick off the top-level HList
, and one to start each individual inner HList
. The inductive step then shows how to add a new item (that we know how to extract from the input) to the last HList
we've added.
import shapeless._, ops.hlist.Selector
trait Picker[I <: HList, O <: HList] {
def apply(i: I): O
}
object Picker {
implicit def hnilPicker[I <: HList]: Picker[I, HNil] = new Picker[I, HNil] {
def apply(i: I) = HNil
}
implicit def hnilHlistPicker[I <: HList, OT <: HList](implicit
picker: Picker[I, OT]
): Picker[I, HNil :: OT] = new Picker[I, HNil :: OT] {
def apply(i: I) = HNil :: picker(i)
}
implicit def hlistPicker[I <: HList, OHH, OHT <: HList, OT <: HList](implicit
sel: Selector[I, OHH],
picker: Picker[I, OHT :: OT]
): Picker[I, (OHH :: OHT) :: OT] = new Picker[I, (OHH :: OHT) :: OT] {
def apply(i: I) = picker(i) match {
case h :: t => (sel(i) :: h) :: t
}
}
}
And then:
def magic[In <: HList, Out <: HList](in: In)(implicit
picker: Picker[In, Out]
): Out = picker(in)
And finally:
scala> println(magic[Input, Output](in))
A(1) :: HNil :: A(1) :: B(b) :: HNil :: A(1) :: C(2,c) :: HNil :: HNil
scala> println(magic[Input, Output](in) == out)
true
It'd be nice to be able to specify only the output type and have the input type inferred, but there's unfortunately no convenient way to implement that.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.