以下代码是Shapeless用例之一的典型演示:
def getHList[P <: Product, F, L <: HList](p: P)(implicit gen: Generic.Aux[P, L]): L = {
gen.to(p)
}
val v = getHList(1, 2, 3, 4, 5, 6, 7, 8, 9)
这给出了正确的结果,不幸的是,它依赖于 scala 的元组句法 suger,并且在参数数量> 22 时不起作用:
val v = getHList(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
(this generates an error that looks this the follow)
[Error] /xxx/HListSuite.scala:41: 29 more arguments than can be applied to method getHList: (p: P)(implicit gen: shapeless.Generic.Aux[P,L])L
one error found
FAILURE: Build failed with an exception.
我想知道是否有宏或其他 scala 功能可以用来打破这个限制,有什么建议吗?
我使用的是 scala 2.12.8,但可以随时升级到 2.13。
-
如果您的目标是生成超过 22 的
HList
,那么有很多方法可以type _23 = Succ[_22] val _23: _23 = new _23 1 :: 2 :: 3 :: 4 :: 5 :: 6 :: 7 :: 8 :: 9 :: 10 :: 11 :: 12 :: 13 :: 14 :: 15 :: 16 :: 17 :: 18 :: 19 :: 20 :: 21 :: 22 :: 23 :: HNil import shapeless.syntax.std.traversable._ import shapeless.ops.hlist.Fill (1 to 23).toHList[the.`Fill[_23, Int]`.Out] (1 to 23).toSizedHList(_23) implicitly[_1 *--* _23].apply //takes long
请注意,其中一些计算需要很长时间。
-
您还可以定义
Product23
、Tuple23
getHList(Tuple23(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23))
尽管仅带括号的语法糖不起作用。案例类的名称并不重要,它可以
MyClass
而不是Tuple23
。 -
在Dotty有TupleXXL。
-
如果你的目标是编写一个像
getHList
这样的方法,你可以尝试让它变得柯里化//libraryDependencies += "com.github.dmytromitin" %% "auxify-macros" % "0.6", scalacOptions += "-Ymacro-annotations" (in 2.13) import com.github.dmytromitin.auxify.macros.{aux, instance} def curriedGetHList[N <: Nat] = new PartiallyAppliedCurriedGetHList[N] class PartiallyAppliedCurriedGetHList[N <: Nat] { def apply[A](a: A)(implicit cghl: CurriedGetHList[N, A]): cghl.Out = cghl(a) } @aux @instance trait CurriedGetHList[N <: Nat, A] { type Out def apply(a: A): Out } object CurriedGetHList { implicit def mkCurriedGetHList[N <: Nat, A] (implicit helper: CurriedGetHListHelper[N, A, A :: HNil] ): Aux[N, A, helper.Out] = instance(a => helper(a :: HNil)) } @aux @instance trait CurriedGetHListHelper[N <: Nat, A, L <: HList] { type Out def apply(acc: L): Out } object CurriedGetHListHelper { implicit def one[A, L <: HList] (implicit reverse: Reverse[L]): Aux[_1, A, L, reverse.Out] = instance(acc => reverse(acc)) implicit def succ[N <: Nat, A, L <: HList] (implicit helper: Lazy[CurriedGetHListHelper[N, A, A :: L]]): Aux[Succ[N], A, L, A => helper.value.Out] = instance(acc => a => helper.value(a :: acc)) } curriedGetHList[_10](1).apply(2)(3)(4)(5)(6)(7)(8)(9)(10)
或
def curriedGetHList[N <: Nat] = new HListBuilder[N, HNil](HNil) class HListBuilder[N <: Nat, L <: HList](l: L) { def apply[A](a: A)(implicit bhl: BuildHList[N, L, A]): bhl.Out = bhl(l, a) } @aux @instance trait BuildHList[N <: Nat, L <: HList, A] { type Out def apply(l: L, a: A): Out } trait LowPriorityBuildHList { implicit def succ[N <: Nat, L <: HList, A]: BuildHList.Aux[Succ[N], L, A, HListBuilder[N, A :: L]] = BuildHList.instance((l, a) => new HListBuilder(a :: l)) } object BuildHList extends LowPriorityBuildHList { implicit def one[L <: HList, A](implicit reverse: Reverse[A :: L]): Aux[_1, L, A, reverse.Out] = instance((l, a) => (a :: l).reverse) } curriedGetHList[_23](1).apply(2).apply(3).apply(4).apply(5).apply(6).apply(7).apply(8).apply(9).apply(10) .apply(11).apply(12).apply(13).apply(14).apply(15).apply(16).apply(17).apply(18).apply(19).apply(20) .apply(21).apply(22).apply(23)
-
或者您可以将长元组划分为元组元组并使用深
Generic
(1 2 3 4(。 -
另一种选择是编写白盒宏
import scala.language.experimental.macros import scala.reflect.macros.whitebox def getHList(xs: Any*): HList = macro getHListImpl def getHListImpl(c: whitebox.Context)(xs: c.Tree*): c.Tree = { import c.universe._ xs.foldRight[Tree](q"_root_.shapeless.HNil: _root_.shapeless.HNil")((h, t) => q"_root_.shapeless.::($h, $t)") } getHList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23)
由于宏是白盒,因此其返回类型是正确的,
Int :: Int :: ... :: HNil
. -
也许最简单的方法是使用
shapeless.ProductArgs
def getHList = new ProductArgs { def applyProduct[L <: HList](l: L): L = l } getHList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23)
实际上
ProductArgs
是通过scala.Dynamic
和白盒宏在Shapeless中实现的。