无形中HList结构的证明

Proof about HList structure in shapeless

有时我需要编译时证明 H <: HList 的每个元素都具有类型 T。 它可以用这个代码表示:

import shapeless._

@annotation.implicitNotFound("Cannot prove that A =:= ${T} forAll A in ${H}")
trait ForAll[H <: HList, T]

object ForAll {

  implicit def head[T]: ForAll[T :: HNil, T] = new ForAll[T :: HNil, T] {}

  implicit def tail[T, HT <: HList](implicit ttail: ForAll[HT, T]): ForAll[T :: HT, T] = new ForAll[T :: HT, T] {}
}
// an example
def chain[H <: HList, A](hlist: H)(a: A)(implicit allAreFunctions: ForAll[H, (A => A)]): A = {
  def iter(h: HList, ax: A): A = h match {
    case HNil => ax
    case (f: (A => A)) :: tail => iter(tail, f(ax))
  }

  iter(hlist, a)
}

val hlist1 = ((_: Int) + 1) :: ((_: Int) * 1) :: ((_: Int) - 2) :: HNil
val hlist2 = ((_: Int).toString) :: ((_: Int) + 1) :: ((_: Int) - 2) ::HNil
chain(hlist1)(1)
// chain(hlist2)(1) doesn't compile

我可以忽略编译器关于可能的匹配错误的警告,因为 ForAll 实例证明代码是正确的。

shapeless 是否实现了类似的东西(或者可能有更好的选择)?

ForAll 只是 shapeless.ops.hlist.LeftReducer 正确定义 Poly。例如

import shapeless.{::, HNil, Poly2}
import shapeless.ops.hlist.LeftReducer

object myPoly extends Poly2 {
  implicit def `case`[A]: Case.Aux[A, A, A] = at((x, y) => x)
}

implicitly[LeftReducer.Aux[Int :: Int :: Int :: Int :: HNil, myPoly.type, Int]]//compiles
implicitly[LeftReducer[Int :: Int :: Int :: Int :: HNil, myPoly.type]]//compiles
//  implicitly[LeftReducer.Aux[Int :: Int :: String :: Int :: HNil, myPoly.type, Int]]//doesn't compile
//  implicitly[LeftReducer[Int :: Int :: String :: Int :: HNil, myPoly.type]]//doesn't compile

LeftReducer[Int :: Int :: Int :: Int :: HNil, myPoly.type].apply(1 :: 2 :: 3 :: 4 :: HNil)//1
//  LeftReducer[Int :: Int :: String :: Int :: HNil, myPoly.type].apply(1 :: 2 :: "a" :: 4 :: HNil)//doesn't compile

(1 :: 2 :: 3 :: 4 :: HNil).reduceLeft(myPoly)//1
//  (1 :: 2 :: "a" :: 4 :: HNil).reduceLeft(myPoly)//doesn't compile