以下代码是无形的用例之一的典型演示:
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
。
如果您的目标是编写类似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 ev: BuildHList[N, L, A]): ev.Out = ev(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].apply(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)
另一个选择是编写一个白盒宏
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
。