在scala无形状库中,当arity> 22(可能使用无形状宏之一)时,是否可以编写通用arity函数?

问题描述 投票:0回答:1

以下代码是无形的用例之一的典型演示:


  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。

scala generic-programming shapeless
1个回答
0
投票

如果您的目标是生成比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

注意,其中一些计算需要很长时间。

还可以定义Product23Tuple23

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

© www.soinside.com 2019 - 2024. All rights reserved.