chessman chessman - 1 month ago 12
Scala Question

Setters for case class in Scala

I want to have a flexible generator for case class.

case class ABC(a: String, b: String, c: String)


I want to write in any order:

val genAbc = (new Gen).withB("b").withA("a").withC("c")


And be able to convert it to case class if all parameters are set:

genAbc.toAbc


If not all parameters are set, I want to get compile error:

(new Gen).withA("a").toAbc //<- error


Is it possible?

Answer

There is a type-safe version of the builder pattern in Scala, see http://blog.rafaelferreira.net/2008/07/type-safe-builder-pattern-in-scala.html

But, if you're willing to give up the requirement to be able to set the parameters in any order, there is a much simpler way:

scala> val abcBuilder = (Abc.apply _).curried
abcBuilder: String => (String => (String => Abc)) = <function1>

That creates a curried function using the Abc class's smart constructor (Abc.apply). A curried function lets you pass in each argument separately. In the interim you keep getting back more functions which take one argument and get you one step closer to an Abc instance. So:

scala> abcBuilder("a")
res11: String => (String => Abc) = <function1>

scala> res11("b")
res12: String => Abc = <function1>

scala> res12("c")
res13: Abc = Abc(a,b,c)

This is all also type-safe (to the extent that you use distinct types for the parameters); so e.g. abcBuilder(1) would give you a compile error.