使用Scala的宏

时间:2017-11-25 04:13:41

标签: scala scala-macros

有一个案例类如下:

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宏可以解决这种冗长问题(我希望如此)。我不太关心默认值;它们可以是随机值。

我想知道是否有人可以给我一个代码示例。谢谢。

编辑:我想澄清更多关于我想要的内容。该函数仅用于单元测试,因此我希望避免使用默认值乱丢我的case类。 (感谢@Tyler指出它。)

Edit2:另外,我想了解更多有关Scala的宏的信息。因此,如果有一个实现这一目标的宏观例子,对我来说这将是一个很好的教训。

3 个答案:

答案 0 :(得分:3)

您不需要宏,只需将新功能添加到您的伴侣对象(或制作另一个构造函数):

case class User(id: Long, name: String, email: String)
object User {
  def defaultUser(): User = {
    User(1L, "name", "email")
  }
}

然后使用它:

val user = User.defaultUser()

编辑:

正如@jwvh指出的那样,您可以将默认参数直接放入案例类中:

case class User(id: Long = 1L, name: String = "tyler", email: String = "tyler@email.com")
val a = User()
val b = User(email = "new@email.com")

但是,正如我在评论中提到的那样,因为看起来你正在创建这个对象的虚拟实例(可能用于测试?),我更喜欢将它与我原来的答案分开。

答案 1 :(得分:1)

我会使用scalacheck-shapeless库但是因为你正在考虑练习一些代码,这里是一个使用类型类的例子,它允许你分离类定义及其默认值。

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和Strings的case类(尽管你可以扩展它)。

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

答案 2 :(得分:0)

最终,我决定使用Scala / Java反射从案例类生成测试数据。

主要优点是(1)编译速度更快(因为它不使用宏),并且(2)API比scalacheck-shapeless更好。

操作方法如下:https://give.engineering/2018/08/24/instantiate-case-class-with-arbitrary-value.html