有没有办法从Shapeless HList检索特定元素的Nat位置?

时间:2019-10-30 06:43:03

标签: scala shapeless

给出任何HList,例如1 :: "str" :: true :: HNil,有一种(简单的)方法来找到与任何特定列表元素(例如,列表元素)的位置相对应的Natf(hlist, true) ==> Nat._2?是否可以对元素 type 而不是元素本身执行相同的操作,例如f[String](hlist) ==> Nat._1?假定每个元素/类型仅发生一次,并且保证了它在列表中的存在。

1 个答案:

答案 0 :(得分:2)

尝试引入类型类

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]

或者使用标准类型类

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