Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
A Builder example in Scala using Phantom Types
case class Food(ingredients: Seq[String])
class Chef[Pizza <: Chef.Pizza] protected (ingredients: Seq[String]) {
import Chef.Pizza._
def addCheese(cheeseType: String): Chef[Pizza with Cheese] = Chef(ingredients :+ cheeseType)
def addTopping(toppingType: String): Chef[Pizza with Topping] = Chef(ingredients :+ toppingType)
def addDough: Chef[Pizza with Dough] = Chef(ingredients :+ "dough")
def build(implicit ev: Pizza =:= FullPizza): Food = Food(ingredients)
object Chef {
sealed trait Pizza
object Pizza {
sealed trait EmptyPizza extends Pizza
sealed trait Cheese extends Pizza
sealed trait Topping extends Pizza
sealed trait Dough extends Pizza
type FullPizza = EmptyPizza with Cheese with Topping with Dough
def apply[T <: Pizza](ingredients: Seq[String]): Chef[T] = new Chef[T](ingredients)
def apply(): Chef[Pizza.EmptyPizza] = apply[Pizza.EmptyPizza](Seq())

This comment has been minimized.

Copy link

@lwilli lwilli commented Apr 28, 2020

Thanks for the great article on this pattern! It looks like this code is a bit different than what is in the article though. Particularly, the apply methods and the protected class constructor don't seem to be in the article. It might be worth mentioning these in the article since the builder is safer because it prevents someone from doing new Chef[Chef.Pizza.FullPizza]().build, which the original code from the article allows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment