Wednesday, July 09, 2008

Type-safe Builder Pattern in Scala

The Builder Pattern is an increasingly popular idiom for object creation. Traditionally, one of it's shortcomings in relation to simple constructors is that clients can try to build incomplete objects, by omitting mandatory parameters, and that error will only show up in runtime. I'll show how to make this verification statically in Scala.


So, let's say you want to order a shot of scotch. You'll need to ask for a few things: the brand of the whiskey, how it should be prepared (neat, on the rocks or with water) and if you want it doubled. Unless, of course, you are a pretentious snob, in that case you'll probably also ask for a specific kind of glass, brand and temperature of the water and who knows what else. Limiting the snobbery to the kind of glass, here is one way to represent the order in scala.
sealed abstract class Preparation  /* This is one way of coding enum-like things in scala */
case object Neat extends Preparation
case object OnTheRocks extends Preparation
case object WithWater extends Preparation

sealed abstract class Glass
case object Short extends Glass
case object Tall extends Glass
case object Tulip extends Glass

case class OrderOfScotch(val brand:String, val mode:Preparation, val isDouble:Boolean, val glass:Option[Glass])

A client can instantiate their orders like this:
val normal = new OrderOfScotch("Bobby Runner", OnTheRocks, false, None)
val snooty = new OrderOfScotch("Glenfoobar", WithWater, false, Option(Tulip));

Note that if the client doesn't want to specify the glass he can pass None as an argument, since the parameter was declared as Option[Glass]. This isn't so bad, but it can get annoying to remember the position of each argument, specially if many are optional. There are two traditional ways to circumvent this problem — define telescoping constructors or set the values post-instantiation with accessors — but both idioms have their shortcomings. Recently, in Java circles, it has become popular to use a variant of the GoF Builder pattern. So popular that it is Item 2 in the second edition of Joshua Bloch's Effective Java. A Java-ish implementation in Scala would be something like this:
class ScotchBuilder {
private var theBrand:Option[String] = None
private var theMode:Option[Preparation] = None
private var theDoubleStatus:Option[Boolean] = None
private var theGlass:Option[Glass] = None

def withBrand(b:Brand) = {theBrand = Some(b); this} /* returning this to enable method chaining. */
def withMode(p:Preparation) = {theMode = Some(p); this}
def isDouble(b:Boolean) = {theDoubleStatus = some(b); this}
def withGlass(g:Glass) = {theGlass = Some(g); this}

def build() = new OrderOfScotch(theBrand.get, theMode.get, theDoubleStatus.get, theGlass);
}

This is almost self-explanatory, the only caveat is that verifying the presence of non-optional parameters (everything but the glass) is done by the Option.get method. If a field is still None, an exception will be thrown. Keep this in mind, we'll come back to it later.

The var keyword prefixing the fields means that they are mutable references. Indeed, we mutate them in each of the building methods. We can make it more functional in the traditional way:
object BuilderPattern {
class ScotchBuilder(theBrand:Option[String], theMode:Option[Preparation], theDoubleStatus:Option[Boolean], theGlass:Option[Glass]) {
def withBrand(b:String) = new ScotchBuilder(Some(b), theMode, theDoubleStatus, theGlass)
def withMode(p:Preparation) = new ScotchBuilder(theBrand, Some(p), theDoubleStatus, theGlass)
def isDouble(b:Boolean) = new ScotchBuilder(theBrand, theMode, Some(b), theGlass)
def withGlass(g:Glass) = new ScotchBuilder(theBrand, theMode, theDoubleStatus, Some(g))

def build() = new OrderOfScotch(theBrand.get, theMode.get, theDoubleStatus.get, theGlass);
}

def builder = new ScotchBuilder(None, None, None, None)
}

The scotch builder is now enclosed in an object, this is standard practice in Scala to isolate modules. In this enclosing object we also find a factory method for the builder, which should be called like so:
import BuilderPattern._

val order = builder withBrand("Takes") isDouble(true) withGlass(Tall) withMode(OnTheRocks) build()

Looking back at the ScotchBuilder class and it's implementation, it might seem that we just moved the huge constructor mess from one place (clients) to another (the builder). And yes, that is exactly what we did. I guess that is the very definition of encapsulation, sweeping the dirt under the rug and keeping the rug well hidden. On the other hand, we haven't gained all the much from this "functionalization" of our builder; the main failure mode is still present. That is, having clients forget to set mandatory information, which is a particular concern since we obviously can't fully trust the sobriety of said clients*. Ideally the type system would prevent this problem, refusing to typecheck a call to build() when any of the non-optional fields aren't set. That's what we are going to do now.

One technique, which is very common in Java fluent interfaces, would be to write an interface for each intermediate state containing only applicable methods. So we would begin with an interface VoidBuilder having all our withFoo() methods but no build() method, and a call to, say, withMode() would return another interface (maybe BuilderWithMode), and so on, until we call the last withBar() for a mandatory Bar, which would return an interface that finally has the build() method. This technique works, but it requires a metric buttload of code — for n mandatory fields 2n interfaces should be created. This could be automated via code generation, but there is no need for such heroic efforts, we can make the typesystem work in our favor by applying some generics magic. First, we define two abstract classes:
abstract class TRUE
abstract class FALSE

Then, for each mandatory field, we add to our builder a generic parameter:
class ScotchBuilder[HB, HM, HD](val theBrand:Option[String], val theMode:Option[Preparation], val theDoubleStatus:Option[Boolean], val theGlass:Option[Glass]) {

/* ... body of the scotch builder .... */

}

Next, have each withFoo method pass ScotchBuilder's type parameters as type arguments to the builders they return. But, and here is where the magic happens, there is a twist on the methods for mandatory parameters: they should, for their respective generic parameters, pass instead TRUE:
class ScotchBuilder[HB, HM, HD](val theBrand:Option[String], val theMode:Option[Preparation], val theDoubleStatus:Option[Boolean], val theGlass:Option[Glass]) {
def withBrand(b:String) =
new ScotchBuilder[TRUE, HM, HD](Some(b), theMode, theDoubleStatus, theGlass)

def withMode(p:Preparation) =
new ScotchBuilder[HB, TRUE, HD](theBrand, Some(p), theDoubleStatus, theGlass)

def isDouble(b:Boolean) =
new ScotchBuilder[HB, HM, TRUE](theBrand, theMode, Some(b), theGlass)

def withGlass(g:Glass) =
new ScotchBuilder[HB, HM, HD](theBrand, theMode, theDoubleStatus, Some(g))
}

The second part of the magic act is to apply the world famous pimp-my-library idiom and move the build() method to an implicitly created class, which will be anonymous for the sake of simplicity:
implicit def enableBuild(builder:ScotchBuilder[TRUE, TRUE, TRUE]) = new {
def build() =
new OrderOfScotch(builder.theBrand.get, builder.theMode.get, builder.theDoubleStatus.get, builder.theGlass);
}

Note the type of the parameter for this implicit method: ScotchBuilder[TRUE, TRUE, TRUE]. This is the point where we "declare" that we can only build an object if all the mandatory parameters are specified. And it really works:
scala> builder withBrand("hi") isDouble(false) withGlass(Tall) withMode(Neat) build()
res5: BuilderPattern.OrderOfScotch = OrderOfScotch(hi,Neat,false,Some(Tall))

scala> builder withBrand("hi") isDouble(false) withGlass(Tall) build()
<console>:9: error: value build is not a member of BuilderPattern.ScotchBuilder[BuilderPattern.TRUE,BuilderPattern.FALSE,BuilderPattern.TRUE]
builder withBrand("hi") isDouble(false) withGlass(Tall) build()

So, we achieved our goal (see the full listing below). If you are worried about the enormous parameter lists inside the builder, I've posted here an alternative implementation with abstract members instead. It is more verbose, but also cleaner.

Now, remember those abstract classes TRUE and FALSE? We never did subclass or instantiate them at any point. If I'm not mistaken, this is an idiom named Phantom Types, commonly used in the ML family of programming languages. Even though this application of phantom types is fairly trivial, we can glimpse at the power of the mechanism. We have, in fact, codified all 2n states (one for each combination of mandatory fields) as types. ScotchBuilder's subtyping relation forms a lattice structure and the enableBuild() implicit method requires the supremum of the poset (namely, ScotchBuilder[TRUE, TRUE, TRUE]). If the domain requires, we could specify any other point in the lattice — say we can doll-out a dose of any cheap whiskey if the brand is not given, this point is represented by ScotchBuilder[_, TRUE, TRUE]. And we can even escape the lattice structure by using Scala inheritance. Of course, I didn't invent any of this; the idea came to me in this article by Matthew Fluet and Riccardo Pucella, where they use phantom types to encode subtyping in a language that lacks it.



object BuilderPattern {
sealed abstract class Preparation
case object Neat extends Preparation
case object OnTheRocks extends Preparation
case object WithWater extends Preparation

sealed abstract class Glass
case object Short extends Glass
case object Tall extends Glass
case object Tulip extends Glass

case class OrderOfScotch(val brand:String, val mode:Preparation, val isDouble:Boolean, val glass:Option[Glass])

abstract class TRUE
abstract class FALSE

class ScotchBuilder
[HB, HM, HD]
(val theBrand:Option[String], val theMode:Option[Preparation], val theDoubleStatus:Option[Boolean], val theGlass:Option[Glass]) {
def withBrand(b:String) =
new ScotchBuilder[TRUE, HM, HD](Some(b), theMode, theDoubleStatus, theGlass)

def withMode(p:Preparation) =
new ScotchBuilder[HB, TRUE, HD](theBrand, Some(p), theDoubleStatus, theGlass)

def isDouble(b:Boolean) =
new ScotchBuilder[HB, HM, TRUE](theBrand, theMode, Some(b), theGlass)

def withGlass(g:Glass) = new ScotchBuilder[HB, HM, HD](theBrand, theMode, theDoubleStatus, Some(g))
}

implicit def enableBuild(builder:ScotchBuilder[TRUE, TRUE, TRUE]) = new {
def build() =
new OrderOfScotch(builder.theBrand.get, builder.theMode.get, builder.theDoubleStatus.get, builder.theGlass);
}

def builder = new ScotchBuilder[FALSE, FALSE, FALSE](None, None, None, None)
}



* Did you hear that noise? It's the sound of my metaphor shattering into a million pieces



EDIT 2008-07-09 at 19h00min: Added introductory paragraph.

1,079 comments:

«Oldest   ‹Older   1001 – 1079 of 1079
Sachin Kumar said...

Thanks for posting, it is really helpful. We are a leading Digital Marketing & Web Designing Company in Delhi. Visit us.
Digital Marketing company in Delhi
Web Designing company in Delhi

Tech Institute said...

This is an informative and knowledgeable article. therefore, I would like to thank you for your effort in writing this article.
Data Scientist Course in Chandigarh

Dev Kumar said...

Very informative blog! There is so much information here that can help thank you for sharing.
Data Science Syllabus

Dev Kumar said...

This is an informative and knowledgeable article. therefore, I would like to thank you for your effort in writing this article.
Business Analytics Course in Chandigarh

Anand said...

So luck to come across your excellent blog, glad i found it. Keep posting new articles. Good luck.
Data Science Course in Jabalpur

Anand said...

I think this is a really good article. You make this information interesting and engaging. Thanks for sharing.
Data Science Training in Jalandhar

James said...

You got a really useful blog I have been here reading for about half an hour. I am a newbie and your post is valuable for me.

dentist near me

Anand said...

There is obviously a lot to know about this. I think you made some good points in Features also. Great job!
Data Science Course in Jalandhar

Aditya said...

After reading your article I was amazed. I know that you explain it very well. I hope that other readers will also experience how I feel after reading your article.
Data Scientist Course in Jalandhar

Aditya said...

Really nice and interesting post. I was looking for this kind of information and enjoyed reading this one. Thanks for sharing.
Data Scientist Course in Jabalpur

Aditya said...

This is definitely one of my favorite blogs. Every post published did impress me.
Mlops Training

HIGS said...


HIGS offers complete research assistance for our clients across the globe. We provide end-to-end PhD research assistance for our clients across all domains with the help of top-notch research experts.
http://higssoftware.com/

Bhuvankumar said...

Great Post, I was looking for this kind of information, Keep posting. Thanks for sharing.
Data Science Courses in Bangalore

Dev Kumar said...

Very informative Blog! There is so much information here that can help thank you for sharing.
Data Analytics Course in Bangalore

Anand said...

Such a very useful article. Very interesting to read the article. I would like to thank you for sharing this awesome article.
Data Analytics Course in Indore

Vijay said...

Excellent Blog! I would like to thank for the efforts you have made in writing this post. I am hoping the same best work from you in the future as well.
Data Scientist Course in Indore

Sunil said...

There is obviously a lot to know about this. I think you made some good points in Features also. Great job!
Mlops Course

Vijay said...

Excellent Blog! I would like to thank for the efforts you have made in writing this post. I am hoping the same best work from you in the future as well
Data Scientist Course in Indore

Bhuvankumar said...

Really impressed! Information shared was very helpful Your website is very valuable. Thanks for sharing.
Business Analytics Course in Bangalore

Tech Institute said...

The blog is informative and very useful therefore, I would like to thank you for your effort in writing this article.
Data Analytics Course in Lucknow

Nathan said...

The information you have posted is very useful and knowledgeable. Thanks for sharing.
Data Analytics Course in Lucknow

Gurudev said...

Very good message. I stumbled across your blog and wanted to say that I really enjoyed reading your articles. Anyway, I will subscribe to your feed and hope you post again soon.
Data Science Course Fees

SKP Knowledge Services said...

Thanks for sharing such a useful post.
Web Development Services in Chennai

kumal kumar said...

Excellent effort to make this blog more wonderful and informative. The information shared was very useful.
Data Analytics Course in Chandigarh

360DigiTMGAurangabad said...

great article!! sharing these type of articles is the nice one and i hope you will share an article on data Analytics. By giving a institute like 360DigiTMG. it is one the best institute for doing certified courses
data analytics course aurangabad

Professional Academic Institute said...

It's like you've got the point right, but forgot to include your readers. Maybe you should think about it from different angles.

Data Science Training in Erode

Professional Course said...

This is just the information I find everywhere. Thank you for your blog, I just subscribed to your blog. It's a good blog.

Best Data Analytics Courses in Bangalore

Professional Academic Institute said...

Very informative message! There is so much information here that can help any business start a successful social media campaign!

Data Scientist Course in Bangalore

Professional Academic Institute said...

It's like you've got the point right, but forgot to include your readers. Maybe you should think about it from different angles.


Data Scientist Course in Gorakhpur

James said...

A useful post shared.
https://www.rundoyen.com/training-plans/

Professional Career Technology said...

Very informative message! There is so much information here that can help any business start a successful social media campaign!

Data Analytics Course in Erode

Dev Kumar said...

We are really grateful for your blog post. You will find a lot of approaches after visiting your post. Great work thank you.
Data Analytics Course in Chandigarh

Professional Academic Institute said...

It's like you've got the point right, but forgot to include your readers. Maybe you should think about it from different angles.

Data Science Course in Gorakhpur

Professional Career Technology said...

Very informative message! There is so much information here that can help any business start a successful social media campaign!

Data Analytics Bangalore

skrishtech said...

Thanks for sharing a valuable information.

Best Interior Designers in Chennai
Best Interior Decorators in Chennai
chennai renovation
flat renovation contractor services in chennai
modular kitchen in chennai
false ceiling contractor services in chennai
carpenter services in chennai

Professional Career Technology said...

Very informative message! There is so much information here that can help any business start a successful social media campaign!

Data Scientist Course Syllabus

Professional Career Technology said...

It's like you've got the point right, but forgot to include your readers. Maybe you should think about it from different angles.


Data Science Course in Kolkata

Career Academic institute said...

Very informative message! There is so much information here that can help any business start a successful social media campaign!


Data Scientist Course in Nashik

Career Academic institute said...

It's like you've got the point right, but forgot to include your readers. Maybe you should think about it from different angles.

Business Analytics Course in Nashik

Professional Course said...

Good blog and absolutely exceptional. You can do a lot better, but I still say it's perfect. Keep doing your best.

Data Science Training in Nagpur

Sunil said...

I am really enjoying reading your well written articles. It looks like you spend a lot of effort and time on your blog. Keep up the good work. Waiting for your next article
Mlops Training

Gurudev said...

I think this is a really good article. You make this information interesting and engaging. Thanks for sharing.
Data Science Course in India

Career Program and Skill Development said...

Very informative message! There is so much information here that can help any business start a successful social media campaign!

Business Analytics Course in Kolkata

Career Program and Skill Development said...

Very informative message! There is so much information here that can help any business start a successful social media campaign!

Data Science Training in Erode

kumal kumar said...

Really nice and amazing post. I was looking for this kind of information, Keep posting. Thanks for sharing.
Data Science Courses in Bangalore

Gurudev said...

Very good message. I stumbled across your blog and wanted to say that I really enjoyed reading your articles. Anyway, I will subscribe to your feed and hope you post again soon.
Data Scientist Course in India

Sunil said...

I want to post a remark that "The quality information of your post is amazing" Great work.
Data Science Course in Jabalpur

Professional Course said...

Hello. I found your blog using msn. This is a very well written article. I'll be sure to bookmark it and come back for more useful information. Thanks for the post. I will definitely be back.

Data Science Training in Bangalore

Professional Course said...

Hello! I just want to give a big thank you for the great information you have here in this post. I will probably come back to your blog soon for more information!

Data Science in Bangalore

Peter Johnson said...

I am inspired with your post writing style & how continuously you describe this topic. micro nutrient for plant

Professional Course said...

What an incredible message this is. Truly one of the best posts I have ever seen in my life. Wow, keep it up.

Data Scientist Training in Bangalore

Akshay said...

I want to post a remark that, The quality information of your post is amazing, Great work.
Data Science Course Details

Professional Course said...

Embark on a journey to achieve your professional goals by enrolling in the Data Scientist course in Bangalore. Learn the skills of collecting, extracting, analyzing, preparing, visualizing, and presenting results to make valuable decisions. Master the concepts of data science through hands-on projects and case studies to learn the latest trends and skills in this field.

Best Data Science Institute in Bangalore

sowmya said...

Actually I read it yesterday but I had some thoughts about it and today I wanted to read it again because it is very well written. data science course in chennai

Natures Pired said...

Jointless Dressing Drums are used for Sterilizing and Storing different types of dressings, instruments, and surgical requisites. Slotted belt with clamp for loosening or tightening of the belt to open or close the perforated on the body.
dental uv chambers

Natures Pired said...

The step of implementing GST in India was a historical move, as it marked a significant indirect tax reform in the country. Almost four and a half years ago, GST replaced 17 local levies like excise duty, service tax, VAT and 13 cesses.
online sole proprietorship registration

Natures Pired said...

Best Hair oil for hair growth

Raghav said...

The new wave of innovation that is changing the way people do business is called data science. Gain expertise in organizing, sorting, and transforming data to uncover hidden patterns Learn the essential skills of probability, statistics, and machine learning along with the techniques to break your data into a simpler format to derive meaningful information. Enroll in Data science in Bangalore and give yourself a chance to power your career to greater heights.
Data Science Training in Bangalore

Raghav said...

Learn to use analytics tools and techniques to manage and analyze large sets of data from Data Science training institutes in Bangalore. Learn to take on business challenges and solve problems by uncovering valuable insights from data. Learn from the comprehensively designed curriculum by the industry experts and work on live projects to sharpen your skills.
Data Scientist Course in Bangalore

Career Program and Skill Development said...

Develop technical skills and become an expert in analyzing large sets of data by enrolling for the Best Data Science course in Bangalore. Gain in-depth knowledge in Data Visualization, Statistics, and Predictive Analytics along with the two famous programming languages and Python. Learn to derive valuable insights from data using skills of Data Mining, Statistics, Machine Learning, Network Analysis, etc, and apply the skills you will learn in your final Capstone project to get recognized by potential employers.

Best Data Science Training institute in Bangalore

Career Programs Excellence said...

With decision-making becoming more and more data-driven, learn the skills necessary to unveil patterns useful to make valuable decisions from the data collected. Also, get a chance to work with various datasets that are collected from various sources and discover the relationships between them. Ace all the skills and tools of Data Science and step into the world of opportunities with the Best Data Science training institutes in Bangalore.

Data Science Course in Bangalore with Placement

Technical Knowledge said...

Very great post which I really enjoy reading this and it is not everyday that I have the possibility to see something like this. Thank You.
Best Online Data Science Courses

Anjali geetha said...

So you cannot say that the future of data science is dim. There are many vacancies announced for data scientists every year.data science training in faridabad

Educational Training and Learning said...

The Data Sciences domain opened up many career opportunities for IT professionals. More and more IT professionals are looking for the best Data Science training to boost their careers. 360DigiTMG is the best place to start your technical training. We are equipped with a world-class curriculum to suit all your technical needs.

Data Science Training in Delhi

Natures Pired said...

Aloe Vera Face Scrub

Courses said...

Really this article is truly one of the best in article history and am a collector of old "items" and sometimes read new items if i find them interesting which is one that I found quite fascinating and should be part of my collection. Very good work!
Data Scientist Course in Gurgaon

Training said...

I like viewing this web page which comprehend the price of delivering the excellent useful resource free of charge and truly adored reading your posting. Thank you!
Data Science Certification Course

Natures Pired said...

Seo services in Gurgaon

Educational Courses said...

Develop technical skills and become an expert in analyzing large sets of data by enrolling for the Best Data Science course in Bangalore. Gain in-depth knowledge in Data Visualization, Statistics, and Predictive Analytics along with the two famous programming languages and Python. Learn to derive valuable insights from data using skills of Data Mining, Statistics, Machine Learning, Network Analysis, etc, and apply the skills you will learn in your final Capstone project to get recognized by potential employers.


Data Science Course in Delhi

Professional Courses and Training said...

The new wave of innovation that is changing the way people do business is called data science. Gain expertise in organizing, sorting, and transforming data to uncover hidden patterns Learn the essential skills of probability, statistics, and machine learning along with the techniques to break your data into a simpler format to derive meaningful information. Enroll in Data science in Bangalore and give yourself a chance to power your career to greater heights.

Data Scientist Course in Delhi

Technical Knowledge said...

I just got to this amazing site not long ago was actually captured with the piece of resources you have got here and big thumbs up for making such wonderful blog page!
Data Scientist Course

test blog said...

Impressive content, will try to make other to see this blog. Thank you for writting this blog for us
website designing company in faridabad
website designing in faridabad

Unknown said...

thanks for your blog
https://www.trainingincoimbatore.in/sap-training-in-coimbatore.html

inoventivedecor said...

Home Interior Designer in Faridabad
home decor items in faridabad
3D Interior Designing Services in Faridabad
Architecture Service in Faridabad
CIVIL INTERIOR DESIGNER IN FARIDABAD

avsmedical said...

Medical Equipment Manufacturers in Medical Equipment Manufacturers in Kolkata should also consider shortening the distance between their factories and their customers as much as possible. If this is not possible, then a company should consider a central distribution point to concentrate shipments to allocate in an efficient manner.

Medical Equipment Manufacturers in Kolkata

digital marketing training malaysia said...

Through this post, I know that your good knowledge in playing with all the pieces was very helpful. I notify that this is the first place where I find issues I've been searching for. You have a clever yet attractive way of writing. digital marketing training malaysia

Eye Care Hospital | Shree Eye Care said...

Thank you for sharing this information. Great work.
best eye hospital in Dehradun

test blog said...

very good writes. feel good after reading this. I really like this. Thank you for this blog post.
Tourist Destination Near Bali
Best Island Around Bali
Best Islands Near Bali
Near Bali Reunion Island Tour Packages
Tourist Destination Near Bali

keshav said...
This comment has been removed by the author.
«Oldest ‹Older   1001 – 1079 of 1079   Newer› Newest»