Skip to content

Instantly share code, notes, and snippets.

@MaximilianoFelice
Created September 6, 2017 22:42
Show Gist options
  • Save MaximilianoFelice/2399538a0c8d918ec46a009f01bdea12 to your computer and use it in GitHub Desktop.
Save MaximilianoFelice/2399538a0c8d918ec46a009f01bdea12 to your computer and use it in GitHub Desktop.
[Medium] [Code] Builder Pattern in Scala with Phantom Types
class Chef[Pizza <: Chef.Pizza](ingredients: Seq[String]) {
import Chef.Pizza._
def addCheese(cheeseType: String): Chef[Pizza with Cheese] = new Chef(ingredients :+ cheeseType)
def addTopping(toppingType: String): Chef[Pizza with Topping] = new Chef(ingredients :+ toppingType)
def addDough: Chef[Pizza with Dough] = new 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
}
}
case class Chef {
...
def build: Food =
if (hasDoughCheeseAndToppings(ingredients)) Food(ingredients)
else throw new FoodBuildingException("You tried to build a pizza without enough ingredients")
}
scala> new Chef().addDough.build
<console>:18: error: Cannot prove that Chef.Pizza.EmptyPizza with Chef.Pizza.Dough =:= Chef.Pizza.FullPizza.
scala> new Chef()
.addCheese("mozzarella")
.addDough
.addTopping("olives")
.build
res1: Food = Food(List(mozzarella, dough, olives))
case class Door[State <: DoorState](){
def open(implicit ev: State =:= Closed) = Door[Open]()
def close(implicit ev: State =:= Open) = Door[Closed]()
}
trait DoorState
case class Open() extends DoorState
case class Closed() extends DoorState
case class Door(state: DoorState) {
def open = state match {
case _: Open => throw new DoorStateException("You cannot open a door thats already open")
case _ => Door(Open())
}
def close = state match {
case _: Closed => throw new DoorStateException("You cannot close a door thats already closed")
case _ => Door(Closed())
}
}
scala> Door[Open]().open
<console>:17: error: Cannot prove that Open =:= Closed.
sealed trait DoorState
sealed trait Open extends DoorState
sealed trait Closed extends DoorState
def open(implicit ev: State =:= Closed) = Door[Open]()
case class Food(ingredients: Seq[String])
implicit val context: Context = ???
def methodThatRequiresContext(str: String)(implicit context: Context) = ???
methodThatRequiresContext("foo")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment