Skip to content

Instantly share code, notes, and snippets.

@jrudolph
Created September 7, 2009 09:30
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save jrudolph/182249 to your computer and use it in GitHub Desktop.
// This code is based on an idea of Rafael de F. Ferreira and was implemented as a response to
// Jim McBeath's blog post http://jim-mcbeath.blogspot.com/2009/09/type-safe-builder-in-scala-part-2.html
// Everyone is free to use, modify, republish, sell or give away this work without prior consent from anybody.
object Scotch {
sealed abstract class Preparation
case object Neat extends Preparation
case object OnTheRocks extends Preparation
case object WithWater extends Preparation
case class OrderOfScotch private[Scotch](val brand:String, val mode:Preparation, val isDouble:Boolean)
trait Option[+X]
case class Some[+X](x:X) extends Option[X]{
def get:X = x
}
trait None extends Option[Nothing] // this differs in the original implementation
case object None extends None
case class Builder[HAS_BRAND<:Option[String],HAS_MODE<:Option[Preparation],HAS_DOUBLE<:Option[Boolean]] private[Scotch]
(brand:HAS_BRAND
,mode:HAS_MODE
,isDouble:HAS_DOUBLE
) {
def ~[X](f:Builder[HAS_BRAND,HAS_MODE,HAS_DOUBLE] => X):X = f(this)
}
def withBrand[M<:Option[Preparation],D<:Option[Boolean]](brand:String)(b:Builder[None,M,D]):Builder[Some[String],M,D] =
Builder(Some(brand),b.mode,b.isDouble)
def withMode[B<:Option[String],D<:Option[Boolean]](mode:Preparation)(b:Builder[B,None,D]):Builder[B,Some[Preparation],D] =
Builder(b.brand,Some(mode),b.isDouble)
def isDouble[B<:Option[String],M<:Option[Preparation]](isDouble:Boolean)(b:Builder[B,M,None]):Builder[B,M,Some[Boolean]] =
Builder(b.brand,b.mode,Some(isDouble))
def build(b:Builder[Some[String],Some[Preparation],Some[Boolean]]):OrderOfScotch =
OrderOfScotch(b.brand.get,b.mode.get,b.isDouble.get)
def builder:Builder[None,None,None] = Builder(None,None,None)
def test {
val x:OrderOfScotch = builder ~ isDouble(true) ~ withMode(Neat) ~ withBrand("Blubber") ~ build
// builder ~ isDouble(true) ~ withMode(Neat) ~ build // fails
// builder ~ isDouble(true) ~ withMode(Neat) ~ withBrand("Blubber") ~ withBrand("Blubber") ~ build // fails
x
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment