Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
688 views
in Technique[技术] by (71.8m points)

scala - Shapeless - turn a case class into another with fields in different order

I'm thinking of doing something similar to Safely copying fields between case classes of different types but with reordered fields, i.e.

case class A(foo: Int, bar: Int)
case class B(bar: Int, foo: Int)

And I'd like to have something to turn a A(3, 4) into a B(4, 3) - shapeless' LabelledGeneric comes to mind, however

LabelledGeneric[B].from(LabelledGeneric[A].to(A(12, 13)))

results in

<console>:15: error: type mismatch;
 found   : shapeless.::[shapeless.record.FieldType[shapeless.tag.@@[Symbol,String("foo")],Int],shapeless.::[shapeless.record.FieldType[shapeless.tag.@@[Symbol,String("bar")],Int],shapeless.HNil]]
    (which expands to)  shapeless.::[Int with shapeless.record.KeyTag[Symbol with shapeless.tag.Tagged[String("foo")],Int],shapeless.::[Int with shapeless.record.KeyTag[Symbol with shapeless.tag.Tagged[String("bar")],Int],shapeless.HNil]]
 required: shapeless.::[shapeless.record.FieldType[shapeless.tag.@@[Symbol,String("bar")],Int],shapeless.::[shapeless.record.FieldType[shapeless.tag.@@[Symbol,String("foo")],Int],shapeless.HNil]]
    (which expands to)  shapeless.::[Int with shapeless.record.KeyTag[Symbol with shapeless.tag.Tagged[String("bar")],Int],shapeless.::[Int with shapeless.record.KeyTag[Symbol with shapeless.tag.Tagged[String("foo")],Int],shapeless.HNil]]
              LabelledGeneric[B].from(LabelledGeneric[A].to(A(12, 13)))
                                                           ^

How do I reorder the fields in the record (?) so this can work with a minimum of boilerplate?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I should leave this for Miles but it's happy hour where I'm from and I can't resist. As he points out in a comment above, the key is ops.hlist.Align, which will work just fine for records (which are just special hlists, after all).

If you want a nice syntax, you need to use a trick like the following for separating the type parameter list with the target (which you want to provide explicitly) from the type parameter list with all the other stuff (which you want to be inferred):

import shapeless._, ops.hlist.Align

class SameFieldsConverter[T] {
  def apply[S, SR <: HList, TR <: HList](s: S)(implicit
    genS: LabelledGeneric.Aux[S, SR],
    genT: LabelledGeneric.Aux[T, TR],
    align: Align[SR, TR]
  ) = genT.from(align(genS.to(s)))
}

def convertTo[T] = new SameFieldsConverter[T]

And then:

case class A(foo: Int, bar: Int)
case class B(bar: Int, foo: Int)

And then:

scala> convertTo[B](A(12, 13))
res0: B = B(13,12)

Note that finding alignment instances will get expensive at compile time for large case classes.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...