使用 Scala 的宏生成一个函数来实例化一个带有默认值的案例 class
Generate a function to instantiate a case class with default values using Scala's macro
有一个案例class是这样的:
case class User(
id: Long,
name: String,
email: String)
我想使用 Scala 宏生成如下函数:
def makeUser(
id: Long = 1L,
name: String = "some name",
email: String = "some email"): User = {
User(
id = id,
name = name,
email = email)
}
它很冗长,Scala 宏可以解决这个冗长(我希望)。我不太关心默认值;它们可以是随机值。
不知道有没有人能给我一个代码示例。谢谢。
编辑:我想更清楚地说明我想要什么。该函数将仅在单元测试中使用,因此我想避免在我的案例 classes 中乱扔默认值。 (感谢@Tyler 指出)。
Edit2:另外,我想了解更多关于 Scala 的宏。因此,如果有一个宏示例可以实现这一点,对我来说将是一个很好的教训。
您不需要宏,只需向您的伴随对象添加一个新函数(或创建另一个构造函数):
case class User(id: Long, name: String, email: String)
object User {
def defaultUser(): User = {
User(1L, "name", "email")
}
}
然后使用它:
val user = User.defaultUser()
编辑:
正如@jwvh 所指出的,您可以将默认参数直接放入您的案例中 class:
case class User(id: Long = 1L, name: String = "tyler", email: String = "tyler@email.com")
val a = User()
val b = User(email = "new@email.com")
但是,正如我在评论中提到的,因为看起来您正在创建此对象的虚拟实例(可能是为了测试?),我更喜欢像我原来的回答那样将它分开。
我会使用 scalacheck-shapeless 库,但由于您正在研究练习一些代码,这里是使用 typeclass 的示例,它允许您将 class 定义及其定义分开默认值。
trait Default[T] {
def get: T
}
object Default {
def apply[T](implicit ev: Default[T]): Default[T] = ev
implicit val user: Default[User] = new Default[User] {
def get: User = User(1,"name","email")
}
}
object Example {
val user = Default[User]().get
}
编辑:对于评论中的问题。我不会使用以下内容,我将其记下来纯粹是为了演示目的。在这一点上,我会使用 scalacheck-shapeless。然而,这已经接近于我假设的 scalacheck 的实现。我认为 Scalacheck 也会带来一些随机性。下面有很多初始样板,但它适用于任何只包含 Int 和字符串的情况 class(尽管你可以扩展它)。
import shapeless._
trait Default[T] {
def get: T
}
object Default {
def apply[T](implicit ev: Default[T]): Default[T] = ev
private def make[T](t: T): Default[T] = new Default[T] {
def get: T = t
}
implicit val int: Default[Int] = make(1)
implicit val string: Default[String] = make("some string")
implicit hnil: Default[HNil] = make(HNil)
implicit hlist[H, T <: HList](implicit hEv: Default[H], tEv: Default[T]): Default[H :: T] = new Default[H :: T] {
def make: H :: T = hEv.get :: tEv.ger
}
implicit gen[CC, R](implicit gen: Generic.Aux[CC, R], rEv: Default[R]): Default[CC] = {
gen.from(rEv.get)
}
}
最终,我决定使用 Scala/Java 反射从案例 类 生成测试数据。
主要优点是 (1) 编译速度更快(因为它不使用宏)和 (2) API 比 scalacheck-shapeless 更好。
操作方法如下:https://give.engineering/2018/08/24/instantiate-case-class-with-arbitrary-value.html
有一个案例class是这样的:
case class User(
id: Long,
name: String,
email: String)
我想使用 Scala 宏生成如下函数:
def makeUser(
id: Long = 1L,
name: String = "some name",
email: String = "some email"): User = {
User(
id = id,
name = name,
email = email)
}
它很冗长,Scala 宏可以解决这个冗长(我希望)。我不太关心默认值;它们可以是随机值。
不知道有没有人能给我一个代码示例。谢谢。
编辑:我想更清楚地说明我想要什么。该函数将仅在单元测试中使用,因此我想避免在我的案例 classes 中乱扔默认值。 (感谢@Tyler 指出)。
Edit2:另外,我想了解更多关于 Scala 的宏。因此,如果有一个宏示例可以实现这一点,对我来说将是一个很好的教训。
您不需要宏,只需向您的伴随对象添加一个新函数(或创建另一个构造函数):
case class User(id: Long, name: String, email: String)
object User {
def defaultUser(): User = {
User(1L, "name", "email")
}
}
然后使用它:
val user = User.defaultUser()
编辑:
正如@jwvh 所指出的,您可以将默认参数直接放入您的案例中 class:
case class User(id: Long = 1L, name: String = "tyler", email: String = "tyler@email.com")
val a = User()
val b = User(email = "new@email.com")
但是,正如我在评论中提到的,因为看起来您正在创建此对象的虚拟实例(可能是为了测试?),我更喜欢像我原来的回答那样将它分开。
我会使用 scalacheck-shapeless 库,但由于您正在研究练习一些代码,这里是使用 typeclass 的示例,它允许您将 class 定义及其定义分开默认值。
trait Default[T] {
def get: T
}
object Default {
def apply[T](implicit ev: Default[T]): Default[T] = ev
implicit val user: Default[User] = new Default[User] {
def get: User = User(1,"name","email")
}
}
object Example {
val user = Default[User]().get
}
编辑:对于评论中的问题。我不会使用以下内容,我将其记下来纯粹是为了演示目的。在这一点上,我会使用 scalacheck-shapeless。然而,这已经接近于我假设的 scalacheck 的实现。我认为 Scalacheck 也会带来一些随机性。下面有很多初始样板,但它适用于任何只包含 Int 和字符串的情况 class(尽管你可以扩展它)。
import shapeless._
trait Default[T] {
def get: T
}
object Default {
def apply[T](implicit ev: Default[T]): Default[T] = ev
private def make[T](t: T): Default[T] = new Default[T] {
def get: T = t
}
implicit val int: Default[Int] = make(1)
implicit val string: Default[String] = make("some string")
implicit hnil: Default[HNil] = make(HNil)
implicit hlist[H, T <: HList](implicit hEv: Default[H], tEv: Default[T]): Default[H :: T] = new Default[H :: T] {
def make: H :: T = hEv.get :: tEv.ger
}
implicit gen[CC, R](implicit gen: Generic.Aux[CC, R], rEv: Default[R]): Default[CC] = {
gen.from(rEv.get)
}
}
最终,我决定使用 Scala/Java 反射从案例 类 生成测试数据。
主要优点是 (1) 编译速度更快(因为它不使用宏)和 (2) API 比 scalacheck-shapeless 更好。
操作方法如下:https://give.engineering/2018/08/24/instantiate-case-class-with-arbitrary-value.html