关于无定型HList结构的证明

时间:2018-05-22 14:34:37

标签: scala 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实例证明代码是正确的。

没有形状可以实现这样的东西(或者有更好的选择)?

1 个答案:

答案 0 :(得分:2)

对于正确定义的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