当参数列表相同时,将一个案例类转换为另一个案例类

Jos*_*ati 7 scala boilerplate

我有很多类似的案例类,它们意味着不同的东西但具有相同的参数列表.

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)
}
Run Code Online (Sandbox Code Playgroud)

鉴于这种设置,我厌倦了编写采用Create类型的参数并返回Created类型的参数的方法.我有大量的测试用例正是这样做的.

我可以写一个函数将一个case类转换成另一个case类.此功能转换User.CreateUser.Created

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

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

def transform[A,B](a: A):B
Run Code Online (Sandbox Code Playgroud)

此外,该功能不应该破坏减少样板的目的.如果更容易使用,请随意为功能建议不同的签名.

Rya*_*yan 10

无形的救援!

您可以使用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
Run Code Online (Sandbox Code Playgroud)

  • @Aivean这可以通过使用`LabelledGeneric`直接修复,这需要案例类参数名称和类型及其顺序的精确对应. (2认同)

Kol*_*mar 5

为了记录,这里是我设法实现的最简单的类型安全语法:

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))
}
Run Code Online (Sandbox Code Playgroud)

它可以像这样使用:

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])
Run Code Online (Sandbox Code Playgroud)

或者同样的事情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]))
Run Code Online (Sandbox Code Playgroud)