【问题标题】:Shapeless: recursive calls in typeclassShapeless:类型类中的递归调用
【发布时间】:2017-01-13 01:39:03
【问题描述】:

在无形类型类方法中实现递归调用的正确方法是什么?

(早期警告:我正在学习无形,所以可能有我还不知道的明显答案/替代方案。非常感谢任何帮助!)

我有一个类型类,它将一个案例类转换为其他对象的嵌套结构——类似于this stackoverflow question 中提到的ToMapRec 示例并受到其启发——除了返回潜在递归 Map 之外,它返回一个由潜在递归成员组成的案例类。所以不要转换MyType的实例:

trait GetsConverted
case class MyType(someData: String, one: GetsConverted, other: GetsConverted) extends GetsConverted
case class MyOtherType(blah: AndSoOn) extends GetsConverted
case class AndSoOn(eventualFinalValue: Int)

进入一个可能递归/嵌套的Map[String,Any](如在另一个问题中),它返回类似于以下实例的内容:

case class ReturnType(name: String, data: Option[Any], more: Set[ReturnType])

要创建more 成员似乎需要在类型类中进行递归调用。但是在另一个方法内部调用类型类的转换方法需要将该函数内部所有类型的隐式参数线程化到最外层调用。因此,而不是像这样的类型类转换:

implicit def hconsToMapRec0[K, V, A <: HList, B <: HList](implicit
  wit: Witness.Aux[K],
  gen: LabelledGeneric.Aux[V, R],
  tmrH: Lazy[ToMapRec[A]],
  tmrT: Lazy[ToMapRec[B]]
): ReturnType = ???

深度三方法(我猜)需要一个函数签名,例如:

implicit def hconsToMapRec0[K, V, A <: HList, B <: HList, W, C <: HList, D <: HList, X, E <: HList, F <: HList](implicit
  wit: Witness.Aux[K],
  gen0: LabelledGeneric.Aux[V, A],
  tmrH0: Lazy[ToMapRec[A]],
  tmrT0: Lazy[ToMapRec[B]],
  gen1: LabelledGeneric.Aux[W, C],
  tmrH1: Lazy[ToMapRec[C]],
  tmrT1: Lazy[ToMapRec[D]],
  gen2: LabelledGeneric.Aux[X, E],
  tmrH2: Lazy[ToMapRec[E]],
  tmrT2: Lazy[ToMapRec[F]]
): ReturnType = ???

或者可能更糟。一般来说,这种方法需要一种方法,该方法的隐含参数乘以递归中尽可能多的深度。并且深度级别的数量仅在运行时才知道。所以这不可能是这样做的。

这感觉类似于 scala 集合库中硬编码的 22 元方法。由于 Shapeless 存在的理由是抽象超过 arity,这似乎是一个问题,需要我迄今为止学到的更多 Shapeless-foo。

所以问题是:你将如何编写一个无形类型类来将任意案例类的结构类似于上面的 MyType 示例转换为递归定义的值,例如:

ReturnType("MyType", Some("someData"), Set(
  ReturnType("MyOtherType", None, Set(
    ReturnType("AndSoOn", Some(10), Set())
  )), 
  ReturnType("MyOtherType", None, Set(
    ReturnType("AndSoOn", Some(20), Set())
  ))
))

【问题讨论】:

    标签: scala shapeless


    【解决方案1】:

    通过下面的实现,我设法得到了与您的示例相近的东西。 其中大部分类似于this问题的答案。不同之处在于它转换为ReturnType,我还为Coproduct 的情况添加了一些情况(这是sealed trait 的通用表示)。

    所以用下面的代码:

    val gen = LabelledGeneric[GetsConverted]
    val m = MyType("someData", MyOtherType(AndSoOn(10)), MyOtherType(AndSoOn(20)))
    val tmr = ToReturnTypeRec[gen.Repr]
    val returnType = tmpr(gen.to(m))
    

    你得到结果

    ReturnType(MyType,Some(someData),Set(
         ReturnType(MyOtherType,None,Set(
             ReturnType(,Some(20),Set())
         )),
         ReturnType(MyOtherType,None,Set(
             ReturnType(,Some(10),Set())
         ))
    ))
    

    这里是实现:

    trait ToReturnTypeRec[L] { def apply(l: L): ReturnType }
    
    trait LowPriorityToReturnTypeRec {
    implicit def hconsToReturnTypeRec1[K <: Symbol, V, T <: HList](implicit
        wit: Witness.Aux[K],
        tmrT: ToReturnTypeRec[T]
      ): ToReturnTypeRec[FieldType[K, V] :: T] = new ToReturnTypeRec[FieldType[K, V] :: T] {
        def apply(l: FieldType[K, V] :: T): ReturnType =
          tmrT(l.tail) match {
            case ReturnType(n,d,m) => ReturnType("", Some(l.head), m)
          }
      }
    }
    
    object ToReturnTypeRec extends LowPriorityToReturnTypeRec {
      def apply[T](implicit tmr: ToReturnTypeRec[T]) = tmr
      implicit val hnilToReturnTypeRec: ToReturnTypeRec[HNil] = new ToReturnTypeRec[HNil] {
        def apply(l: HNil): ReturnType = ReturnType("", None, Set())
      }
    
      implicit def hconsToReturnTypeRec0[K <: Symbol, V, T <: HList, R](implicit
        // wit: Witness.Aux[K],
        gen: LabelledGeneric.Aux[V, R],
        tmrH: Lazy[ToReturnTypeRec[R]],
        tmrT: ToReturnTypeRec[T]
      ): ToReturnTypeRec[FieldType[K, V] :: T] = new ToReturnTypeRec[FieldType[K, V] :: T] {
        def apply(l: FieldType[K, V] :: T): ReturnType =
          tmrT(l.tail) match {
            case ReturnType(n,d,m) => ReturnType(n, d, m + tmrH.value(gen.to(l.head)))
          }
      }
    
      implicit val cnillToReturnTypeRec: ToReturnTypeRec[CNil] = new ToReturnTypeRec[CNil] {
        def apply(c: CNil): ReturnType = ReturnType("Impossible", None, Set())
      }
    
      implicit def cconsToReturnTypeRec0[K <: Symbol, V <: Product, T <: Coproduct](implicit
        wit: Witness.Aux[K],
        tmrH: Lazy[ToReturnTypeRec[V]],
        tmrT: Lazy[ToReturnTypeRec[T]]
      ): ToReturnTypeRec[FieldType[K,V] :+: T] = new ToReturnTypeRec[FieldType[K, V] :+: T] {
        def apply(c: FieldType[K,V] :+: T): ReturnType = {
          c match {
            case Inl(h) => tmrH.value(h) match {
              case ReturnType(_,d,m) => ReturnType(wit.value.name, d, m)
            }
            case Inr(t) => tmrT.value(t)
          }
        }
      }
    
      implicit def genericToReturnTypeRec[P, R]( implicit
        gen: LabelledGeneric.Aux[P, R],
        tmr: Lazy[ToReturnTypeRec[R]]
      ): ToReturnTypeRec[P] = new ToReturnTypeRec[P] {
        def apply(p: P): ReturnType = tmr.value(gen.to(p))
      }
    }
    

    【讨论】:

    • 谢谢! +1 努力,但我可能没有很好地突出原始问题中需要递归的位置。这在我们都链接到的同一个问题中得到了很好的定义。但是当调用站点位于另一个(递归)函数中时,问题就来了。这样做一次很简单,但是您将如何递归调用:val returnType = tmpr(gen.to(m))
    猜你喜欢
    • 1970-01-01
    • 2014-02-13
    • 1970-01-01
    • 1970-01-01
    • 2013-03-03
    • 2023-03-23
    • 1970-01-01
    • 2018-05-30
    • 2021-10-14
    相关资源
    最近更新 更多