I’ll take a side of REPL with that
“Would you like to make that interactive?”
– The eternal question facing documentation writers.
Most code documentation has either static code with fixed results or dynamic editor blocks with evaluated results. This post explores a third option, a sidebar REPL that coexists with static examples, letting you copy interesting snippets and build on them.
Persistent Workspace Benefits
The sidebar REPL mirrors the intuitive “one environment, one REPL” model, keeping state across interactions.
(kind/hiccuprequire '[reagent.core :as r])
'((defonce state (r/atom {:zaniness "moderate"
(:mood "curious"}))))
We start with some initial mood and zaniness. Here’s a suggestion to modify it:
swap! state assoc :zaniness "maximum") (
Click the “copy” icon in the code example to transfer it to the REPL window.
And now we’ll add to the post a mini-app for monitoring the state.
(kind/hiccup:div.card {:style {:margin "20px 0"
[:max-width "600px"}}
:div.card-header.bg-success.text-white
[:h5.mb-0 "🔍 State Monitor"]]
[:div.card-body
[:dl.row.mb-0
[:dt.col-sm-3.text-end "Current state:"]
[:dd.col-sm-9
[:div.p-2.bg-light.border.rounded.font-monospace.small
[fn [] (pr-str @state))]]]
['(:dt.col-sm-3.text-end "Instructions:"]
[:dd.col-sm-9.text-muted.small
["Try: " [:div.sourceCode [:code "(swap! state assoc :key \"value\")"]] " in the REPL"]]]])
🔍 State Monitor
- Current state:
- Instructions:
- Try: in the REPL
(swap! state assoc :key "value")
The state you create in the REPL affects page components.
Curated and Open Exploration
Authors can provide focused examples for specific learning goals, while readers can pursue tangents. Here’s an example showcasing Clojure’s frequencies
function with some delicious data:
frequencies ["apple"
("banana"
"apple"
"cherry"
"banana"
"apple"])
"apple" 3, "banana" 2, "cherry" 1} {
Try it with different data, maybe the letters in your name.
frequencies "supercalifragilisticexpialidocious") (
Or simulate random selections:
frequencies (repeatedly 1000
(rand-nth [:a :b :c :d :e :f]))) #(
Visual Playground
The REPL can affect components on the page. This interactive canvas demonstrates how data can drive visual elements.
(kind/hiccup:div.card {:style {:margin "20px 0"}}
[:div.card-header.bg-primary.text-white
[:h5.mb-0 "🎨 Interactive Canvas"]]
[:div.card-body
[:p.mb-3 "Shapes controlled by " [:code "@state"] " — use the REPL to create and modify them."]
[fn []
['(:svg {:xmlns "http://www.w3.org/2000/svg"
[:width "100%"
:height "250px"
:viewBox "0 0 500 250"
:style {:border "2px solid #dee2e6" :border-radius "4px" :background "#f8f9fa"}}
;; Grid pattern
:defs
[:pattern {:id "grid" :width "50" :height "50" :patternUnits "userSpaceOnUse"}
[:path {:d "M 50 0 L 0 0 0 50" :fill "none" :stroke "#e9ecef" :stroke-width "1"}]]]
[:rect {:width "500" :height "250" :fill "url(#grid)"}]
[;; Dynamic shapes from state
for [{:keys [type x y size color]} (:shapes @state)]
(case type
(:circle [:circle {:cx x :cy y :r size :fill color :stroke "#333" :stroke-width 1}]
:square [:rect {:x (- x size) :y (- y size)
:width (* 2 size) :height (* 2 size)
:fill color :stroke "#333" :stroke-width 1}]
:triangle [:polygon {:points (str x "," (- y size) " "
- x size) "," (+ y size) " "
(+ x size) "," (+ y size))
(:fill color :stroke "#333" :stroke-width 1}]
nil))])]]])
🎨 Interactive Canvas
Shapes controlled by @state
— use the REPL to create and modify them.
Add a single shape (maybe a tiny orange dot? 🟠):
swap! state update :shapes conj
(:type :circle :x 250 :y 200 :size 15 :color "#f39c12"}) {
Click the “copy” icon in the code example to transfer it to the REPL window, then press CTRL+Enter to eval.
Add more shapes by modifying the :shapes
vector:
swap! state update :shapes into
(:type :circle :x 100 :y 100 :size 30 :color "#e74c3c"}
[{:type :square :x 200 :y 150 :size 25 :color "#3498db"}
{:type :triangle :x 350 :y 100 :size 40 :color "#2ecc71"}]) {
Generate a ✨random constellation✨:
swap! state assoc :shapes
(repeatedly 15
(hash-map :type (rand-nth [:circle :square :triangle])
#(:x (rand-int 500)
:y (rand-int 250)
:size (+ 8 (rand-int 25))
:color (rand-nth ["#e74c3c" "#3498db" "#2ecc71"
"#f39c12" "#9b59b6" "#1abc9c"]))))
The Full Development Experience
While the sidebar REPL provides convenient experimentation for simple examples, the real power of Clay is in working with the source code. Clone the repository and open it in your editor to get the full interactive notebook experience. You can modify examples, create new namespaces, and contribute ideas back to the community. Add your own namespace under src/
and it is published as a new post.
Wrapping Up
I hope you enjoyed exploring this sidebar REPL approach. The sidebar REPL complements Clay’s strength of showing examples and results. Authors can intersperse static examples with outputs and interactive prompts for experimentation. The real magic happens when you clone this repository and work with the source code directly.
Ready to contribute? Add your own namespace under src/
and it becomes a new post automatically.
Join the conversation: The best place to discuss this feature, share improvements, or ask questions is the Zulip: clay-dev channel
Give it a try and let us know what you think! 🚀
source: src/civitas/repl.clj