|
338 | 338 | ;; inspectable. How do we get the compositional approach of AoG while keeping everything |
339 | 339 | ;; as simple Clojure data? |
340 | 340 |
|
| 341 | +;; ## 📖 The Algebraic Foundation in Clojure |
| 342 | +;; |
| 343 | +;; Remarkably, Clojure's standard `merge` and `concat` already exhibit distributive-like |
| 344 | +;; properties similar to Julia's `*` and `+`: |
| 345 | + |
| 346 | +;; **Merge distributes over concat:** |
| 347 | +(let [shared {:data :penguins} |
| 348 | + layers [{:plottype :scatter} {:plottype :line}]] |
| 349 | + (map #(merge shared %) layers)) |
| 350 | + |
| 351 | +;; This mirrors the algebraic property from AoG: |
| 352 | +;; ```julia |
| 353 | +;; data(penguins) * (scatter + line) |
| 354 | +;; = (data(penguins) * scatter) + (data(penguins) * line) |
| 355 | +;; ``` |
| 356 | + |
| 357 | +;; **Concat preserves structure:** |
| 358 | +(concat [{:x :a}] [{:y :b}]) |
| 359 | + |
| 360 | +;; **Merge combines properties:** |
| 361 | +(merge {:x :a} {:y :b}) |
| 362 | + |
| 363 | +;; So we can build `=*` (merge-based) and `=+` (concat-based) that preserve |
| 364 | +;; these algebraic properties while working with plain Clojure data. The algebra |
| 365 | +;; isn't imposed artificially—it emerges naturally from Clojure's data operations. |
| 366 | + |
341 | 367 | ;; # Design Overview |
342 | 368 | ;; |
343 | 369 | ;; Before diving into implementation, let's establish what we're building and how it works. |
|
793 | 819 | ;; ## ⚙️ Malli Registry Setup |
794 | 820 | ;; |
795 | 821 | ;; Create a registry that includes both default schemas and malli.util schemas. |
796 | | -;; This enables declarative schema utilities like :merge, :union, :select-keys. |
| 822 | +;; This enables declarative schema utilities like `:merge`, `:union`, `:select-keys`. |
797 | 823 |
|
798 | 824 | (def registry |
799 | 825 | "Malli registry with default schemas and util schemas (for :merge, etc.)" |
|
1172 | 1198 | (throw (ex-info "Layer validation failed" |
1173 | 1199 | errors)))) |
1174 | 1200 |
|
1175 | | -;; **Why this works**: |
1176 | | -;; |
1177 | | -;; - Standard `merge` composes correctly (flat structure) |
1178 | | -;; - No collision with data columns (`:=plottype` ≠ `:plottype`) |
1179 | | -;; - All standard library operations work: `assoc`, `update`, `mapv`, `filter`, `into` |
1180 | | - |
1181 | 1201 | ;; # API Implementation |
1182 | 1202 | ;; |
1183 | 1203 | ;; This section implements the core API: the composition operators (`=*`, `=+`), |
@@ -2982,12 +3002,12 @@ iris |
2982 | 3002 | ;; 1. Each function detects whether its first argument is a plot spec |
2983 | 3003 | ;; or data (dataset, map-of-vectors, vector-of-maps) |
2984 | 3004 | ;; 2. If data: converts to a plot spec first via `(data ...)` |
2985 | | -;; 3. If a plot spec: merges the new specification using `*` |
| 3005 | +;; 3. If a plot spec: merges the new specification using `=*` |
2986 | 3006 | ;; 4. Everything returns plot specs, so threading works naturally |
2987 | 3007 | ;; |
2988 | 3008 | ;; **Both styles work**: |
2989 | 3009 | ;; |
2990 | | -;; You can use compositional style with `*`: |
| 3010 | +;; You can use compositional style with `=*`: |
2991 | 3011 | ;; ```clojure |
2992 | 3012 | ;; (=* (data penguins) (mapping :x :y) (scatter)) |
2993 | 3013 | ;; ``` |
@@ -3795,13 +3815,6 @@ iris |
3795 | 3815 | ;; 3. Points are colored by sex, but regressions computed per species |
3796 | 3816 | ;; 4. This shows that grouping and color are independent concepts |
3797 | 3817 |
|
3798 | | -;; ## 🧪 Example 8: Using `plot` for Spec Inspection and Customization |
3799 | | - |
3800 | | -;; Most of the time, layers auto-display and you don't need `plot`. |
3801 | | -;; But sometimes you want the raw target spec for debugging or customization. |
3802 | | -;; See the Multi-Target Rendering section for examples of using `plot` |
3803 | | -;; with `:vl` and `:plotly` targets. |
3804 | | - |
3805 | 3818 | ;; # Faceting |
3806 | 3819 | ;; |
3807 | 3820 | ;; Small multiples: splitting data across rows and columns for comparison. |
@@ -3903,8 +3916,8 @@ iris |
3903 | 3916 | ;; 3. Compute domains across all facets (for shared scales) |
3904 | 3917 | ;; 4. Render each facet as mini-plot |
3905 | 3918 | ;; |
3906 | | -;; For :geom target - compute layout positions manually |
3907 | | -;; For :vl/:plotly targets - could use their grid layout features |
| 3919 | +;; For `:geom` target - compute layout positions manually |
| 3920 | +;; For `:vl`/`:plotly` targets - could use their grid layout features |
3908 | 3921 |
|
3909 | 3922 | ;; ## 🧪 Examples |
3910 | 3923 |
|
@@ -4093,7 +4106,7 @@ iris |
4093 | 4106 |
|
4094 | 4107 | ;; 1. Y-axis forced to extend from 0 to 40 instead of auto-computed range from 10.4 to 33.9 |
4095 | 4108 | ;; 2. Useful for starting axes at meaningful values (like 0) |
4096 | | -;; 3. Custom domains compose via `*` operator |
| 4109 | +;; 3. Custom domains compose via `=*` operator |
4097 | 4110 |
|
4098 | 4111 | ;; Custom domains on both axes |
4099 | 4112 | (plot |
@@ -5455,7 +5468,7 @@ iris |
5455 | 5468 | ;; |
5456 | 5469 | ;; **Core Design**: |
5457 | 5470 | ;; - Layers as flat maps with `:=...` distinctive keys (see [Design Exploration](#design-exploration)) |
5458 | | -;; - Composition using `*` (merge) and `+` (overlay) |
| 5471 | +;; - Composition using `=*` (merge) and `=+` (overlay) |
5459 | 5472 | ;; - Standard library operations work natively |
5460 | 5473 | ;; - Backend-agnostic IR |
5461 | 5474 | ;; |
|
0 commit comments