Clojure in a box
A Clojure box runs Clojure in the same document, and the same process, as the Kotlin boxes around it. It has outputs, can be launched and stopped, and can reach everything in Field.
Field runs Clojure 1.11.3 inside its own JVM. A Clojure box can hold a reference to the Box it
is running in, hand an FLine to Field's renderer, and call anything in Field's standard library
directly, with nothing to convert and no second runtime to start.
A Clojure box is a child of a Clojure root
To make a Clojure box, connect it beneath a Clojure root. Select a box and run
Set Main Language: Clojure from the command palette (set-main-language-clojure). Every box
childed to it from then on runs as Clojure. This works the same way it does for
Python:
- The root itself is not Clojure. Only its children are. Clojure typed into the root box is compiled as Kotlin, and fails.
- A box stays Clojure while it has any Clojure-root parent, so a box childed to both the document root and a Clojure root runs as Clojure.
The setting is saved with the document, so the arrangement survives a reload.
Output lands beside the form that made it
The block below is a Clojure box's code, and the picture under it is that box's editor.
:::static
;; Each line of output lands beside the form that made it.
(println "clojure" (clojure-version))
(def modules {:app [:ui :store], :ui [:util], :store [:util]})
(println (count (mapcat val modules)) "edges")

A box is evaluated one top-level form at a time, so each line of output is placed beside the form that was running when it was printed.
A box that prints nothing shows its last value
When a box prints nothing, the value of its last form appears beside it instead:
:::static
;; Nothing is printed, so the value of the last form is what you see.
(->> (range 1 11)
(map #(* % %))
(reduce +))

The value is printed the way Clojure prints it, so a vector appears as [1 2 3] rather than
[1, 2, 3]. As in a Python cell, it is not shown when
the box has already printed something, or when the value is nil (which is what println and
(lines …) return). A def returns the var it defined, so a box that only defines things shows
that var (#'live.user/spiral), as a REPL would.
One namespace, every box
There is one Clojure runtime for the whole Field process, and every Clojure box in every open
document evaluates in one namespace, live.user. Two Clojure boxes are like two cells of one
notebook: a def in one box is visible in the next, and state persists between runs. It also
means a name you thought you had deleted is still there, and that two boxes can overwrite each
other's names.
clojure.core and Field's own field.core are referred into the namespace. Nothing else is added:
there is no per-box prelude and no implicit (ns …).
What a box can reach
Clojure's Java interop already reaches all of Field, and most of the time that is what to use:
(.getName (box)) ; box properties and topology
(.getChildren (box))
(doto (afield.geometry.path.FLine.) ; Field's own types
(.moveTo 0 0 0)
(.lineTo 100 100 0))
Interop is awkward for two common Kotlin shapes: extension functions, which Clojure sees as static
methods on a class named after a file, and default arguments, which Clojure has to pass in full.
field.core wraps the ones you need most, and is already referred into every box:
| form | what it does |
|---|---|
(box) |
Returns the Box this code is running in. |
(display v), (display channel v) |
Renders a value in an output panel using Field’s value renderer. Supply a channel name to update that output independently. |
(lines ls :w 700 :h 350 :fit true) |
Draws the supplied FLine collection (or a single FLine) in a 700 × 350 output panel named lines. :fit true fits the drawing to the view; :channel "name" draws into a different panel. |
(polyline [[x y] …]) |
Builds an FLine through the points in sequence. |
(color line r g b a), (color line [r g b a]), (thickness line w) |
Sets line colour (RGBA, 0 to 1) or stroke thickness in pixels and returns the line, so -> threads. |
(animate f) |
Builds a per-frame animation around f. Return the animation from the box to launch it. |
(frame), (end) |
Inside an animation body: reads the current frame number, or stops now. |
(prop "name"), (prop! "name" v) |
Reads or writes a property of this box. prop! returns the value it wrote. |
(prop b "name"), (prop! b "name" v) |
The same, for another box b, such as one from (.getParents (box)). |
(slider label f :min 0 :max 1 :step 0.01 :value v) |
Draws a slider in the box's controls panel. f is called with each new value; it can also be an atom, which is set to the value. |
(checkbox label f :value false) |
Draws a checkbox; f (a function or an atom) receives true or false. |
(context :name), (context :name default) |
Reads the nearest property, or Kotlin variable, called name on this box or its parents. |
(context SomeClass) |
Finds the nearest value of that class on this box or its parents, like fromContext in Kotlin. |
Properties are the box's own storage, and they outlive a run:
:::static
;; The box is a JVM object. Its properties are its storage, and they outlive a run.
(def me (box))
(prop! "runs" (inc (or (prop "runs") 0)))
(println (str (.getName me) ": run number " (prop "runs")
", " (count (prop "code")) " characters of Clojure"))
(display "summary" {:name (.getName me) :runs (prop "runs") :language (prop "code_language")})

Run it twice and the count goes up: the second run is the same namespace talking to the same box.
Clojure's own libraries are on the classpath too (clojure.string, clojure.set, clojure.walk,
clojure.pprint, clojure.edn, clojure.data.json, clojure.tools.reader), so require works as
usual. You cannot add a new dependency from inside a box: the classpath is fixed when Field starts.
:::static
;; Clojure's own libraries are on Field's classpath, so `require` is just `require`.
(require '[clojure.string :as str]
'[clojure.set :as set]
'[clojure.data.json :as json])
(println (str/join " / " (map name (sort (set/union #{:a :b} #{:b :c})))))
(println (json/write-str {:box (.getName (box)) :forms 3}))

Drawing
An FLine is Field's geometry type and a Clojure box makes them like anything else. polyline
builds one from points, color and thickness style it, and lines puts it in an output panel.
:::static
;; A spiral, built in Clojure, drawn by Field.
(def spiral
(polyline (for [i (range 500)
:let [t (* i 0.05)
r (* t 5.5)]]
[(+ 350 (* r (Math/cos t)))
(+ 175 (* r (Math/sin t)))])))
(println (count (.getNodes spiral)) "points")
(lines [(-> spiral (color 0.98 0.86 0.3 0.9) (thickness 2))] :w 700 :h 350)

(.getNodes spiral) works because polyline returns a real FLine, with all of its own methods.
Everything on
Drawing with FLines is reachable from a Clojure box by interop; field.core
only covers the awkward corners.
Sliders and checkboxes
slider and checkbox draw the same controls a Kotlin box's slider(...) and checkbox(...) draw.
Each takes a label and what to call when the control moves: a function of one argument, or an atom,
which is set to the new value. Keyword options set the range, step and starting value:
:::static
;; Moving the slider or ticking the box updates the atom, and the watch redraws.
(def state (atom {:freq 3.0 :square false}))
(defn redraw [{:keys [freq square]}]
(display "main" (format "%s at %.1f Hz" (if square "square" "sine") freq)))
(slider "frequency" #(swap! state assoc :freq %) :min 1 :max 10 :step 0.1 :value 3.0)
(checkbox "square wave" #(swap! state assoc :square %))
(add-watch state :redraw (fn [_ _ _ now] (redraw now)))
(redraw @state)

The controls appear when the box finishes running, all in one controls panel; pass
:channel "name" to put a control in a panel of its own. Run the box again and a control you moved
keeps its value, and its function is called with that value, so state agrees with what is on the
screen. A function that throws is reported on the line the control was created on.
Values from parent boxes
A box can read what the boxes above it provide. (context :speed) looks for a property called
speed on this box, then on its parents, then on theirs, and returns the first it finds. A Kotlin
parent's top-level variables count too, so a Clojure box can use a value a Kotlin box computed.
Give a second argument to use when nothing is found, or a class instead of a name to find the
nearest value of that type:
(context :speed) ; a parent's `speed` property, or nil
(context "palette" []) ; a parent Kotlin box's `val palette`, or []
(context afield.graphics.vulkan.Renderer) ; the renderer this box is under
The clojure_box_parity document has a Kotlin root with a
speed property and a palette variable, and a Clojure box that reads both.
Launching an animation
A Clojure box animates the way a Kotlin box does: returning an animation from the box starts it.
(animate f) builds one that calls f once per frame. So (animate …) has to be the last form,
and you launch the box rather than run it. See
Frames and the Animation Loop.
:::static
;; Returning an animation is what launches it.
(def n 160)
(animate
(fn []
(let [t (* 0.08 (frame))]
(display "frames" (str "frame " (frame)))
(lines [(-> (polyline (for [i (range n)]
[(* 4.4 i) (+ 100 (* 70 (Math/sin (+ t (* i 0.06)))))]))
(color 0.35 0.78 1.0 0.95)
(thickness 2))]
:w 720 :h 200))))

The function runs every frame; (def n 160) ran once, when the box was launched.
Stop ends an animation. It does not interrupt a Clojure form that is still evaluating, so avoid
an infinite loop in code that runs before the animation.
When something throws
An error is reported against the form that threw, not the top of the box:
:::static
(println "this line runs")
(def parts 0)
(/ 100 parts)
(println "this line does not")

The error sits on the line of the form that threw. The forms above it have already run and printed, and the forms below it do not run.
When the throw happens inside a function defined in the box, the error is placed on the line inside that function where it happened, rather than on the line that called it:
:::static
(def readings [])
(defn mean [values]
(/ (reduce + values) (count values)))
(mean readings)

Anything a box writes to *err* appears the same way, as an error against the line that wrote it.
That is how Clojure's own warnings reach you, such as the one for shadowing a clojure.core name
with a def.
A syntax error, such as an unclosed parenthesis, is reported with its line and column before
anything is evaluated, so none of the box runs. Other mistakes (an unresolvable symbol, a macro that
throws, a call with the wrong number of arguments) show up only when the form runs, after the forms
above it have run. A Clojure box therefore has no equivalent of the red underline a Kotlin box gets
before it runs. The message names the problem first and the place second, for example
Unable to resolve symbol: no-such-function in this context — Syntax error compiling at (3:1).:
:::static
(println "before")
(no-such-function 3)

The editor knows the language
Clojure code is highlighted, and the parentheses in the figures above are coloured by nesting depth, as they are in every Field editor.
Completion comes from Compliment. It knows the
names in the box's namespace (even in a box that has never been run), shows each function's argument
vector and docstring, and offers local names in scope at the caret: a let binding, a fn
parameter or a doseq variable. The popup below was opened at the prefix sp:

Each row carries its argument vector, and the panel on the right shows the docstring. spline under
LOCAL is the let binding two lines up, and spiral under VALUES is a var an earlier box on this
page defined in the shared namespace.
The editor also shows what each form evaluated to the last time the box ran: rest the cursor on a form and the note beside it shows the value in bold with its type, how long ago that was, and whether the form has changed since. The value is recorded as the box runs, not by evaluating the form again, so an unrealised lazy sequence is named rather than printed. See Line notes.
What is not here
- No per-box namespaces. Every box shares
live.user, as above. - No nREPL, and no connecting an editor to Field's runtime. The box is the REPL.
- No
add-libs. The classpath is fixed at process start. - No compile-time diagnostics. See the note under "When something throws".
- Stop does not interrupt an evaluation, only an animation.
See also Python in a Box, Lua in a Box and language extensions.