【问题标题】:In scala shapeless library, is it possible to write a generic arity function when the arity > 22 (presumably using one of shapeless macros)?在 scala 无形库中,是否可以在 arity > 22 时编写通用的 arity 函数(大概使用无形宏之一)?
【发布时间】:2020-09-19 05:56:32
【问题描述】:

以下代码是 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。

【问题讨论】:

    标签: scala generic-programming shapeless hlist arity


    【解决方案1】:
    • 如果您的目标是生成大于 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

    • 在 Dotty 中有TupleXXL

    • 如果您的目标是编写像 getHList 这样的方法,您可以尝试将其 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)
      

      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)
      
    • 或者你可以将长元组分成元组的元组并使用深度Generic1234)。

    • 另一种选择是编写白盒宏

      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和whitebox宏在Shapeless中实现的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-31
      • 2015-10-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多