Converting one case class to another when the list of arguments is the same

I have many similar classes that mean different things but have the same list of arguments.

object User { case class Create(userName:String, firstName: String, lastName: String) case class Created(userName:String, firstName: String, lastName: String) } object Group { case class Create(groupName:String, members: Int) case class Created(groupName:String, members: Int) } 

Given this setup, I'm tired of writing methods that take an argument of type Create and return an argument of type Created. I have many test cases that do just such things.

I could write a function to convert one case class to another. This function converts User.Create to User.Created

 def userCreated(create: User.Create) = User.Create.unapply(create).map((User.Created.apply _).tupled).getOrElse(sys.error(s"User creation failed: $create")) 

I had to write another such function for a group. What I really would like to get is a generic function that takes two types of case classes and an object from one case class and transforms into another. Sort of,

 def transform[A,B](a: A):B 

In addition, this function should not exceed the goal of reducing the template. Please feel free to suggest a different signature for this feature if it is easier to use.

+6
source share
2 answers

For the record, here is the simplest typeafe syntax that I managed to implement:

 implicit class Convert[A, RA](value: A)(implicit ga: Generic.Aux[A, RA]) { def convertTo[B, RB](gb: Generic.Aux[B, RB])(implicit ev: RA =:= RB) = gb.from(ga.to(value)) } 

And it can be used as follows:

 case class Create(userName: String, firstName: String, lastName: String) case class Created(userName: String, firstName: String, lastName: String) val created = Create("foo", "bar", "baz").convertTo(Generic[Created]) 

Or the same with LabelledGeneric to provide better type safety:

 implicit class Convert[A, RA](value: A)(implicit ga: LabelledGeneric.Aux[A, RA]) { def convertTo[B, RB](gb: LabelledGeneric.Aux[B, RB])(implicit ev: RA =:= RB) = gb.from(ga.to(value)) } val created = Create("foo", "bar", "baz").convertTo(LabelledGeneric[Created])) 
+5
source

Lifeless salvation!

You can use Shapeless Generic to create general representations of case classes, which can then be used to accomplish what you are trying to do. Using LabelledGeneric , we can use both types and parameter names.

 import shapeless._ case class Create(userName: String, firstName: String, lastName: String) case class Created(userName: String, firstName: String, lastName: String) case class SortOfCreated(screenName: String, firstName: String, lastName: String) val c = Create("username", "firstname", "lastname") val createGen = LabelledGeneric[Create] val createdGen = LabelledGeneric[Created] val sortOfCreatedGen = LabelledGeneric[SortOfCreated] val created: Created = createdGen.from(createGen.to(c)) sortOfCreatedGen.from(createGen.to(c)) // fails to compile 
+10
source

All Articles