Last active
January 9, 2025 21:26
-
-
Save MaximilianoFelice/55ffa549172799fd359415a79a1f2d17 to your computer and use it in GitHub Desktop.
A Builder example in Scala using Phantom Types
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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 doingnew Chef[Chef.Pizza.FullPizza]().build
, which the original code from the article allows.