有没有办法从 Shapeless HList 中检索特定元素的 Nat 位置?
Is there a way to retrieve Nat position of a specific element from Shapeless HList?
给定任何 HList
,例如1 :: "str" :: true :: HNil
,有没有一种(简单的)方法可以找到对应于任何特定列表元素位置的 Nat
,例如f(hlist, true) ==> Nat._2
?是否可以对元素 type 而不是元素本身做同样的事情,例如f[String](hlist) ==> Nat._1
?假设每个 element/type 只出现一次并且它在列表中的存在是有保证的。
尝试引入一种类型class
import shapeless.{::, DepFn0, HList, HNil, Nat, Succ}
import shapeless.nat._
trait Find[L <: HList, A] extends DepFn0 { type Out <: Nat }
object Find {
type Aux[L <: HList, A, Out0 <: Nat] = Find[L, A] { type Out = Out0 }
def instance[L <: HList, A, Out0 <: Nat](x: Out0): Aux[L, A, Out0] = new Find[L, A] {
type Out = Out0
override def apply(): Out = x
}
implicit def head[A, T <: HList]: Aux[A :: T, A, _0] = instance(_0)
implicit def tail[H, T <: HList, A, N <: Nat](implicit
find: Aux[T, A, N]
): Aux[H :: T, A, Succ[N]] = instance(Succ[N]())
}
def f[L <: HList, A](l: L, a: A)(implicit find: Find[L, A]): find.Out = find()
def f[A] = new PartiallyApplied[A]
class PartiallyApplied[A] {
def apply[L <: HList](l: L)(implicit find: Find[L, A]): find.Out = find()
}
implicitly[Find.Aux[Int :: String :: Boolean :: HNil, String, _1]]
implicitly[Find.Aux[Int :: String :: Boolean :: HNil, Boolean, _2]]
val hlist = 1 :: "str" :: true :: HNil
val n = f(hlist, true)
implicitly[n.N =:= _2]
val m = f[String](hlist)
implicitly[m.N =:= _1]
或标准类型 classes
import shapeless.ops.hlist.{Collect, IsHCons, ZipWithIndex}
import shapeless.{HList, HNil, Nat, Poly1, poly}
trait Second[A] extends Poly1
object Second {
implicit def cse[A, N <: Nat]: poly.Case1.Aux[Second[A], (A, N), N] = poly.Case1(_._2)
}
def f[L <: HList, A, L1 <: HList, L2 <: HList, N <: Nat](l: L, a: A)(implicit
zipWithIndex: ZipWithIndex.Aux[L, L1],
collect: Collect.Aux[L1, Second[A], L2],
isHCons: IsHCons.Aux[L2, N, _]
): N = collect(zipWithIndex(l)).head
给定任何 HList
,例如1 :: "str" :: true :: HNil
,有没有一种(简单的)方法可以找到对应于任何特定列表元素位置的 Nat
,例如f(hlist, true) ==> Nat._2
?是否可以对元素 type 而不是元素本身做同样的事情,例如f[String](hlist) ==> Nat._1
?假设每个 element/type 只出现一次并且它在列表中的存在是有保证的。
尝试引入一种类型class
import shapeless.{::, DepFn0, HList, HNil, Nat, Succ}
import shapeless.nat._
trait Find[L <: HList, A] extends DepFn0 { type Out <: Nat }
object Find {
type Aux[L <: HList, A, Out0 <: Nat] = Find[L, A] { type Out = Out0 }
def instance[L <: HList, A, Out0 <: Nat](x: Out0): Aux[L, A, Out0] = new Find[L, A] {
type Out = Out0
override def apply(): Out = x
}
implicit def head[A, T <: HList]: Aux[A :: T, A, _0] = instance(_0)
implicit def tail[H, T <: HList, A, N <: Nat](implicit
find: Aux[T, A, N]
): Aux[H :: T, A, Succ[N]] = instance(Succ[N]())
}
def f[L <: HList, A](l: L, a: A)(implicit find: Find[L, A]): find.Out = find()
def f[A] = new PartiallyApplied[A]
class PartiallyApplied[A] {
def apply[L <: HList](l: L)(implicit find: Find[L, A]): find.Out = find()
}
implicitly[Find.Aux[Int :: String :: Boolean :: HNil, String, _1]]
implicitly[Find.Aux[Int :: String :: Boolean :: HNil, Boolean, _2]]
val hlist = 1 :: "str" :: true :: HNil
val n = f(hlist, true)
implicitly[n.N =:= _2]
val m = f[String](hlist)
implicitly[m.N =:= _1]
或标准类型 classes
import shapeless.ops.hlist.{Collect, IsHCons, ZipWithIndex}
import shapeless.{HList, HNil, Nat, Poly1, poly}
trait Second[A] extends Poly1
object Second {
implicit def cse[A, N <: Nat]: poly.Case1.Aux[Second[A], (A, N), N] = poly.Case1(_._2)
}
def f[L <: HList, A, L1 <: HList, L2 <: HList, N <: Nat](l: L, a: A)(implicit
zipWithIndex: ZipWithIndex.Aux[L, L1],
collect: Collect.Aux[L1, Second[A], L2],
isHCons: IsHCons.Aux[L2, N, _]
): N = collect(zipWithIndex(l)).head