Having the below builder pattern in Scala. To simplify it, I'm using 3 instances of A
such that instance1
contains only field1
and has no connection to field2
or field3
. The problem is that everywhere in the code I have to use val s = A.instance1.field1.get; doSomething(s)
, where the get
call is not potentially safe. For example A.instance1.field2.get
would fail on None.get
. In order to guard it I have to match case against the option and deal with None cases:
object A {
val instance1 = new ABuilder().withField1("abc").build1
val instance2 = new ABuilder().withField1("abc").withField2("def").build2
val instance3 = new ABuilder().withField1("abc").withField3("def").build1
}
case class A(builder: ABuilder) {
val field1: Option[String] = builder.field1
val field2: Option[String] = builder.field2
val field3: Option[String] = builder.field3
}
class ABuilder {
var field1: Option[String] = None
var field2: Option[String] = None
var field3: Option[String] = None
def withField1(f: String): ABuilder = {
this.field1 = Some(f)
this
}
def withField2(f: String): ABuilder = {
this.field2 = Some(f)
this
}
def withField3(f: String): ABuilder = {
this.field3 = Some(f)
this
}
def build1: A = {
require(field1.isDefined, "field 1 must not be None")
A(this)
}
def build2: A = {
require(field1.isDefined, "field 1 must not be None")
require(field2.isDefined, "field 2 must not be None")
A(this)
}
}
Another solution would be to use parameterized types, also called phantom types. I found very few good tutorials on that subject, and could not find in any of them how to implement a type safe builder pattern in Scala with phantom types and actual data (or state) - all examples describe methods only.
How can I use phantom types in my example to avoid getting runtime None
exceptions and get only nice type-mismatch exceptions? I'm trying to parameterize all the classes and methods mentioned and use sealed traits but had no success so far.