Prototype-Style Copying with Immutable Data

Reuse immutable base values in Clojure by deriving new variants through update and merge instead of mutable cloning.

The prototype pattern creates new values by copying an existing example and then applying small changes. In Clojure, this pattern becomes very natural because immutable data is already easy to reuse safely.

You usually do not need “cloneable objects.” You need a stable base value and a clear way to derive variants from it.

Why The Pattern Feels Different In Clojure

In mutable OO systems, prototype often solves the cost or complexity of constructing a new object from scratch. In Clojure, a prototype is usually just a base map, record, or nested data structure that you transform with assoc, update, merge, or cond->.

That gives you two major benefits:

  • the original value stays intact
  • the new value makes its differences explicit

A Simple Example

 1(ns app.report)
 2
 3(def base-report
 4  {:report/title "Weekly Summary"
 5   :report/format :pdf
 6   :report/filters {:region :all
 7                    :include-archived false}
 8   :report/options {:include-charts true
 9                    :include-notes false}})
10
11(defn make-report [overrides]
12  (merge base-report overrides))
13
14(def eu-report
15  (-> base-report
16      (assoc :report/title "EU Weekly Summary")
17      (assoc-in [:report/filters :region] :eu)
18      (assoc-in [:report/options :include-notes] true)))

base-report acts as the prototype. New reports are derived from it by applying explicit changes.

Why This Is Better Than Rebuilding Everything

Prototype-style construction helps when:

  • many values share the same baseline shape
  • only a few fields differ each time
  • the shared default configuration should stay obvious
  • callers should start from a known-good template

This is especially useful for:

  • configuration templates
  • test fixtures
  • report definitions
  • UI or document defaults
  • command or job templates

Structural Sharing Helps

Clojure’s persistent data structures already use structural sharing internally. That means deriving a new map from a prototype often avoids a full deep copy in practice. You still write the code in a simple data-oriented way, and the runtime avoids more duplication than a naive mental model might suggest.

So the pattern is not just aesthetically natural in Clojure. It also fits the performance model reasonably well for many cases.

When To Be Careful

Prototype-style copying can become misleading when the base value grows too magical. If the prototype is huge and the overrides are tiny, readers may struggle to understand what the derived value really is.

It also becomes risky when invariants span several nested fields. In that case, raw assoc calls may be too weak. A factory or builder step that validates the derived result can be safer.

Prototype vs Builder

Use a prototype when you start from a known-good base and apply a few targeted differences.

Use a builder when the value is better assembled in stages and the sequence itself is part of the clarity.

The two patterns can combine. A builder can start from a prototype, then enrich and validate the result.

Design Review Questions

Ask these before leaning on prototype-style derivation:

  • Is the base value truly stable and reusable?
  • Are the differences small and easy to see?
  • Will readers understand the derived value without chasing too much hidden context?
  • Do you need validation after applying overrides?

If those answers are good, prototype-style copying is one of the cleanest Clojure creational tools available.

Loading quiz…
Revised on Thursday, April 23, 2026