Reputation: 1923
There is a case class that looks like this:
case class User(
id: Long,
name: String,
email: String)
I want to use Scala macro to generate a function like below:
def makeUser(
id: Long = 1L,
name: String = "some name",
email: String = "some email"): User = {
User(
id = id,
name = name,
email = email)
}
It's verbose, and Scala macro can solve this verbosity (I hope). I don't care much about the default values; they can be random values.
I wonder if anyone can give me a code example. Thank you.
Edit: I'd like to clarify more about what I want. The function will only be used in unit tests, so I'd like to avoid littering my case classes with default values. (Thanks @Tyler for pointing it out).
Edit2: Also, I'd like to learn more about Scala's Macro. Therefore, if there's a macro example that achieves this, it would be a good lesson for me.
Upvotes: 0
Views: 2131
Reputation: 1923
Eventually, I've decided to use Scala/Java reflection for generating test data from case classes.
The main advantages are (1) compile faster (because it doesn't use Macros) and (2) the API is nicer than scalacheck-shapeless.
Here's how to do it: https://give.engineering/2018/08/24/instantiate-case-class-with-arbitrary-value.html
Upvotes: 0
Reputation: 746
I would use the scalacheck-shapeless library but since you were looking into practising some code here is an example of using a typeclass which would allow you to seperate the class definition and its default value.
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
}
EDIT: For the question in the comment. I wouldn't use the following, I'm putting it down purely for demontstrative purposes. At this point I would use scalacheck-shapeless. However, this is getting close to what I assume is scalacheck's implementation. Scalacheck will throw some randomness around too I assume. The following has a lot of initial boilerplate but it will work for any case class that only contains Int and Strings (though you could extend that).
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)
}
}
Upvotes: 1
Reputation: 18157
You don't need a macro, just add a new function to your companion object (or make another constructor):
case class User(id: Long, name: String, email: String)
object User {
def defaultUser(): User = {
User(1L, "name", "email")
}
}
Then use it:
val user = User.defaultUser()
Edit:
As @jwvh pointed out you can put default parameters right into your case class:
case class User(id: Long = 1L, name: String = "tyler", email: String = "[email protected]")
val a = User()
val b = User(email = "[email protected]")
But, as I mentioned in my comment, since it looks like you are creating a dummy instance of this object (maybe for testing?), I prefer to have it separated like in my original answer.
Upvotes: 3