Skip to content

Instantly share code, notes, and snippets.

@BrianLondon
Last active September 6, 2017 19:50
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 BrianLondon/57954d01130c965d17404f31db4ad3b3 to your computer and use it in GitHub Desktop.
Save BrianLondon/57954d01130c965d17404f31db4ad3b3 to your computer and use it in GitHub Desktop.
// Compile time validated builder pattern using type level programming
sealed trait Fur
case object Short extends Fur
case object Long extends Fur
case object Curly extends Fur
case class Dog(name: String, age: Int, fur: Fur)
sealed trait Bool
trait True extends Bool
trait False extends Bool
class DogBuilder[HasName <: Bool, HasAge <: Bool, HasFur <: Bool](
val name: Option[String],
val age: Option[Int],
val fur: Option[Fur])
object DogBuilder {
def apply() = new DogBuilder[False, False, False](None, None, None)
implicit def withNameBuilder[HasAge <: Bool, HasFur <: Bool](db: DogBuilder[False, HasAge, HasFur]) = new {
def withName(name: String) = new DogBuilder[True, HasAge, HasFur](Some(name), db.age, db.fur)
}
implicit def withAgeBuilder[HasName <: Bool, HasFur <: Bool](db: DogBuilder[HasName, False, HasFur]) = new {
def withAge(age: Int) = new DogBuilder[HasName, True, HasFur](db.name, Some(age), db.fur)
}
implicit def withFurBuilder[HasName <: Bool, HasAge <: Bool](db: DogBuilder[HasName, HasAge, False]) = new {
def withFur(fur: Fur) = new DogBuilder[HasName, HasAge, True](db.name, db.age, Some(fur))
}
implicit def realizeBuilder(db: DogBuilder[True, True, True]) = new {
def toDog: Dog = Dog(db.name.get, db.age.get, db.fur.get)
}
}
/* Example User */
val fidoBuilder = DogBuilder()
val fido = fidoBuilder.withAge(3).withFur(Short).withName("fido").toDog
val namelessBuilder = DogBuilder()
val nameless = namelessBuilder.withAge(7).withFur(Curly).toDog // this won't compile because name is missing
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment