Builder Pattern

I'm collecting a handful of useful patterns mostly for my own reference when building safe and maintainable software. I also have a note on the spooky Phantom Type pattern.

nqthqn_procedural_generative_building_cubism_8087aeef-bd02-4865-8dcd-ee705925a26c.jpg

Why?

The builder pattern helps prevent misconfiguration of a data structure and offers a pipeline friendly interface. How does it accomplish this?

  • Hides internal module implementation using an opaque type
  • Exposes pipeline friendly with functions
  • Uses sane initial defaults

Implementation

-- opaque
type X = X C

-- hidden internal configuration
type alias C = { f : String, g : Bool }

-- sane configuration defaults
init : X
init =
    X { f = "default", g = False }

-- create variants
withF : String -> X -> X
withF f (X c) =
    X { c | f = f }

withG : Bool -> X -> X
withG g (X c) =
    X { c | g = g }

Usage

x = init |> withF "custom"

or...

x = 
    init
        |> withG True
        |> withF "custom"
	

Use cases

This pattern is great for design systems. A view can have a nice default and as business needs change and pain points emerge variants can be easily introduced and internal configuration can be updated smoothly.

card |> withTitle "Elm Trees"

Downsides

This can yield lots of boilerplate for larger configurations. A with function is needed for updating each part of the configuration — it is intentionally not exposed by the module.

Who decides the sanity or sensibility for the defaults? You might run into a case where everyplace the module is used it has a with function tagging along. Maybe a great reason to refactor.

And of course — there is always a tradeoff between flexibility and rigidity when it comes to selecting a pattern that suites your need.

Builder Pattern
Interactive graph
On this page
Why?
Implementation
Usage
Use cases
Downsides