Shapeless:检查多态函数的类型约束(提升类型)

Shapeless : Checking of the Type Constraints of Polymorphic Functions (lifted types)

我正在编写内部 DSL,并使用 Shapeless 来强制类型安全。但是,我遇到了一个问题。

问题的简化版如下

考虑下面的代码片段:

import shapeless._
import syntax.std.function._
import ops.function._

implicit class Ops[P <: Product, L <: HList](p: P)(implicit val gen: Generic.Aux[P, L]) {
  def ~|>[F, R](f: F)(implicit fp: FnToProduct.Aux[F, L ⇒ R]) =
    f.toProduct(gen.to(p))
}

(1, 2) ~|> ((x: Int, y: Int) ⇒ x + y) // -> at compile time, it ensures the types are aligned.

(1, 2, 3) ~|> ((x: Int, y: Int, z: Int) ⇒ x + y + z) // -> compiles okay

(1, "2", 3) ~|> ((x: Int, y: Int, z: Int) ⇒ x + y + z) // -> gives a compile error, as expected.

但是,我想使用容器类型 Place[A],而不是 A。

case class Place[A](a: A)

val a = Place[Int](1)
val b = Place[Int]("str")

并确保类型与类型参数对齐。

(a, b) ~|> ((x: Int, y: String) ⇒ x.toString + y)

也就是说,在上面的例子中,我想根据Place[_]的类型参数来检查类型,在上面的例子中,分别是IntString .

非常感谢您的帮助!

您可以结合使用 Unwrapped and LiftAll

Unwrapped 允许您提取 AnyVal 的内容,LiftAllHList 的每个条目调用给定类型 class。如果我正确理解您要执行的操作,它可能如下所示:

case class Place[A](a: A) extends AnyVal // cuz why not?

implicit class Ops[P <: Product, L <: HList, U <: HList, C <: HList](p: P)
  (implicit
    val g: Generic.Aux[P, L],
    val l: LiftAll.Aux[Unwrapped, L, U],
    val c: Comapped.Aux[Place, L, C]
  ) {
    def ~|>[F, R](f: F)(implicit fp: FnToProduct.Aux[F, C ⇒ R]) = {
      def fuckitZip(l1: HList, l2: HList): HList = (l1, l2) match {
        case (h1 :: t1, h2 :: t2) => (h1, h2) :: fuckitZip(l1, l2)
        case _ => HNil
      }
      def fuckitMap(l: HList, f: Any => Any): HList = l match {
        case HNil   => HNil
        case h :: t => f(h) :: fuckitMap(t, f)
      }
      def f(a: Any): Any = a match {
        case (x: Any, y: Any) =>
          x.asInstanceOf[Unwrapped[Any] { type U = Any }].unwrap(y)
      }
      val zp = fuckitZip(g.to(p), l.instances)
      val uw = fuckitMap(zp, f _).asInstanceOf[C]
      f.toProduct(uw)
    }
}

请注意,这里我使用了 Comapped 和一个不安全的 zip/map 来保持简单,使用适当的 HList 使其成为类型安全的 zip/map 保留为练习。

与这些复杂的转换一样,通过内联所有内容,使用专用类型 class 重新实现所有内容可能会更简单(并且会 run/compiler 更快),我只是想证明这是可行的使用原始操作:)