Skip to content

Instantly share code, notes, and snippets.

@gjuhasz86
Created March 3, 2016 19:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gjuhasz86/70cb1ca2cc057dac5ba7 to your computer and use it in GitHub Desktop.
Save gjuhasz86/70cb1ca2cc057dac5ba7 to your computer and use it in GitHub Desktop.
Type-safe builder
/**
* Type-safe builder pattern.
* - setters can be called in any order
* - setting something twice is a compile error
* - calling build on an incomplete object is compile error
* - custom error messages are emitted to describe the exact problem
*/
object BuilderExample {
def main(args: Array[String]): Unit = {
val p =
Person.builder
.age(26)
.address("London")
.name("Joe")
.build
}
}
case class Person(name: String, age: Int, address: String)
sealed trait Field[+T]
case class Set[+T](value: T) extends Field[T]
sealed trait Unset extends Field[Nothing]
case object Missing extends Unset
object Person {
@implicitNotFound(msg = "You're trying to set the 'name' field twice")
type is0[-From, +To] = <:<[From, To]
@implicitNotFound(msg = "You're trying to set the 'age' field twice")
type is1[-From, +To] = <:<[From, To]
@implicitNotFound(msg = "You're trying to set the 'address' field twice")
type is2[-From, +To] = <:<[From, To]
@implicitNotFound(msg = "Object is incomplete")
type is3[-From, +To] = <:<[From, To]
case class Builder[NameT <: Field[String], AgeT <: Field[Int], AddressT <: Field[String]](
_name: NameT, _age: AgeT, _address: AddressT) {
def name(n: String)(implicit ev: NameT is0 Unset) =
this.copy(_name = Set(n))
def age(a: Int)(implicit ev: AgeT is1 Unset) =
this.copy(_age = Set(a))
def address(a: String)(implicit ev: AddressT is2 Unset) =
this.copy(_address = Set(a))
def build(implicit ev1: NameT is3 Set[String], ev2: AgeT is3 Set[Int], ev3: AddressT is3 Set[String]) =
Person(_name.value, _age.value, _address.value)
}
val builder = Builder(Missing, Missing, Missing)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment