> 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
```elm
-- 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
```elm
x = init |> withF "custom"
```
or...
```elm
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.
```elm
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.