当参数列表相同时,将一种情况 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]))
我有很多类似的案例 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]))