The following code is a typical demo of one of shapeless' use case:
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)
This gives the proper result, unfortunately, it relies on scala's tuple syntactic suger, and doesn't work when the number of argument > 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.
I wonder if there is a macro or another scala feature I can use to break this limitation, any advice?
I'm using scala 2.12.8 but can upgrade to 2.13 at any time.
If your goal is to generate an HList
longer than 22 then there are plenty of ways
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
Be careful, some of these calculations take long.
Also you can define 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))
although syntax sugar just with brackets will not work. The name of case class isn't significant, it can be MyClass
instead of Tuple23
.
In Dotty there is TupleXXL.
If your goal is to write a method like getHList
you can try to make it curried
//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)
or
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)
Or you can divide long tuple into tuple of tuples and use deep Generic
(1 2 3 4).
One more option is to write a whitebox macro
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)
Since the macro is whitebox its return type will be proper, Int :: Int :: ... :: HNil
.
Maybe the easiest is to use 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)
Actually ProductArgs
are implemented in Shapeless via scala.Dynamic
and whitebox macro.