当参数列表相同时,将一种情况 class 转换为另一种情况

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

我有很多类似的案例 classes,它们的意思不同,但参数列表相同。

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)
}

鉴于这种设置,我厌倦了编写采用 Create 类型参数和 return Created 类型参数的方法。我有大量的测试用例可以做这种事情。

我可以编写一个函数将一种情况 class 转换为另一种情况。此函数将 User.Create 转换为 User.Created

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

我不得不为 Group 编写另一个这样的函数。 我真正想要的是一个通用函数,它接受两种类型的 case classes 和一个 case class 的对象并转换为另一种。像,

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

此外,此功能不应违背减少样板文件的目的。如果更易于使用,请随时为函数建议不同的签名。

无形拯救!

您可以使用 Shapeless 的 Generic 创建案例 类 的通用表示,然后可以使用它来完成您想要做的事情。使用 LabelledGeneric 我们可以强制执行类型和参数名称。

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

郑重声明,这是我设法实现的最简单的类型安全语法:

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))
}

并且可以这样使用:

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])

或与 LabelledGeneric 相同以实现更好的类型安全:

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]))