Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transform one case class into another when the argument list is the same

I have a lot of similar case classes which mean different things but have the same argument list.

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 kind of a setup, I was tired of writing methods that take an argument of type Create and return an argument of type Created. I have tons of test cases that do exactly this kind of thing.

I could write a function to convert one case class into the other. This function converts User.Create into 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 Group. What I'd really like to have is a generic function that takes the two types of the case classes and an object of one case class and converts into the other. Something like,

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

Also, this function shouldn't defeat the purpose of reducing boilerplate. Please feel free to suggest a different signature for the function if that's easier to use.

like image 362
Joseph Nuthalapati Avatar asked Aug 11 '15 18:08

Joseph Nuthalapati


1 Answers

Shapeless to the rescue!

You can use Shapeless's Generic to create generic representations of case classes, that can then be used to accomplish what you're trying to do. Using LabelledGeneric we can enforce 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
like image 200
Ryan Avatar answered Nov 02 '22 20:11

Ryan