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[_]的类型参数来检查类型,在上面的例子中,分别是Int
和String
.
非常感谢您的帮助!
您可以结合使用 Unwrapped
and LiftAll
。
Unwrapped
允许您提取 AnyVal
的内容,LiftAll
为 HList
的每个条目调用给定类型 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 更快),我只是想证明这是可行的使用原始操作:)
我正在编写内部 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[_]的类型参数来检查类型,在上面的例子中,分别是Int
和String
.
非常感谢您的帮助!
您可以结合使用 Unwrapped
and LiftAll
。
Unwrapped
允许您提取 AnyVal
的内容,LiftAll
为 HList
的每个条目调用给定类型 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 更快),我只是想证明这是可行的使用原始操作:)