【问题标题】:In Shapeless, given two records, how do I require that both records have the same keys and join them?在 Shapeless 中,给定两条记录,我如何要求两条记录具有相同的键并将它们连接起来?
【发布时间】:2016-09-30 03:59:12
【问题描述】:

假设我有两条记录。一个可能是案例类的LabelledGeneric 表示;而另一个可能是程序员提供的记录,提供人类可读的字段标签:

case class Book(author: String, title: String, quantity: Int)
val labels = ('author ->> "Author") :: ('title ->> "Title") :: ('quantity ->> "Quantity") :: HNil

有没有办法

  1. 要求Book的标记通用表示和labels的记录类型拥有相同的键(或至少label的键是Book的键的子集)和
  2. “加入”或通过键将它们压缩在一起,这样您就可以得到与左侧参数具有相同键的记录,值是一对(lhs 值,Option[rhs 值])或类似的东西?

我认为这可以通过为每一方提取Keys 见证,然后使用Align 的组合来实现。 (我希望看到它被添加到开箱即用的无形操作中。)这允许我们将“元数据”与类的字段相关联(例如,代替使用注释)。

【问题讨论】:

    标签: scala shapeless


    【解决方案1】:

    我认为这可行,但我很想听听 cmets:

    trait ZipByKey[L <: HList, R <: HList] extends DepFn2[L, R] {
      type Out <: HList
    }
    
    object ZipByKey {
    
      type Aux[L <: HList, R <: HList, O <: HList] = ZipByKey[L, R] { type Out = O }
    
      implicit def hnilZip[R <: HList] = new ZipByKey[HNil, R] { type Out = HNil; override def apply(l: HNil, r: R) = HNil }
    
      implicit def hlistZip[K, V, T <: HList, R <: HList, RV, Remainder <: HList, TO <: HList]
      (implicit
        remover: Remover.Aux[R, K, (RV, Remainder)],
        recurse: ZipByKey.Aux[T, Remainder, TO]
      ) = new ZipByKey[FieldType[K, V] :: T, R] {
        type Out = FieldType[K, (V, RV)] :: TO
    
        def apply(l: FieldType[K, V] :: T, r: R): Out = {
          val (rv, remainder) = remover.apply(r)
          val newValue = (l.head, rv)
          labelled.field[K](newValue) :: recurse.apply(l.tail, remainder)
        }
      }
    }
    

    示例用法:

      case class Book(author: String, title: String, quantity: Int)
      val labels = ('author ->> "Author") :: ('title ->> "Title") :: ('quantity ->> "Number Of") :: HNil
    
      val generic = LabelledGeneric[Book]
    
      def zipByKey[T, G <: HList, R <: HList, O <: HList](t: T, r: R)
        (implicit generic: LabelledGeneric.Aux[T, G],
          zipByKey: ZipByKey.Aux[G, R, O]): O = {
        zipByKey.apply(generic.to(t), r)
      }
    
      println(zipByKey(Book("Hello", "Foo", 3), labels))
    

    打印出来

    (Foo,Id) :: (Bar,Name) :: (3,Number Of) :: HNil
    

    如果我们不想让所有键都出现在labels 中,那么还有一些工作要做。但可能还有其他方法可以处理。

    【讨论】:

    • 我想到使用Remover 来消除R 中的已用完的密钥可能没有必要。我从其他一些操作中找到了它。我想知道是否有某种编译时性能优势?我可能会尝试在编辑中删除它。
    • 现在我意识到使用RemoverR 中删除已用完的键对于实现“外连接”很有用:一旦我们有了R = HNil,我们就可以附加剩余的L的尾巴
    • 你的方法很棒!
    • 这个解决方案有个奇怪的问题:每对产生的第二个元素的类型是Nothing
    猜你喜欢
    • 1970-01-01
    • 2022-10-05
    • 1970-01-01
    • 2015-06-22
    • 2020-07-07
    • 2019-06-30
    • 1970-01-01
    • 2023-03-24
    • 1970-01-01
    相关资源
    最近更新 更多