Read Hara and build from scratch
This path is for learning how programs are constructed, not merely memorising a new syntax.
No previous Lisp or Clojure knowledge is assumed. Hara gives you a small reading model, a live evaluator, and direct access to the ideas beneath a framework: values, functions, decisions, collections, state, and effects.
Keep a Hara REPL or the browser Playground open while you read. Type each example, predict the result, change one value, and run it again.
The whole reading rule
Section titled “The whole reading rule”Start with one form:
(+ 19 23); => 42Hara reads the expression between the parentheses, calls + with two numbers, and returns 42.
(operation input input)That operation-first shape continues through ordinary function calls. You do not need to learn a separate surface syntax for each kind of application.
Values are the material of a program
Section titled “Values are the material of a program”Programs receive, create, name, and transform values.
Numbers and text
Section titled “Numbers and text”(+ 19 23); => 42
(str "hello, " "Ada"); => "hello, Ada"The first form combines numbers. The second combines strings. Both follow the same reading rule.
Ordered values
Section titled “Ordered values”A vector uses square brackets:
(def scores [10 20 30])
(count scores); => 3def gives a value a name. A vector is useful when order matters: positions, steps, coordinates, rows, or a sequence of commands.
Named facts
Section titled “Named facts”A map uses braces. Keywords label each fact:
(def player {:name "Nova" :score 0})
(get player :name); => "Nova"A map can represent a player, task, request, document, screen, or configuration. It keeps related facts in one inspectable value.
Transform values instead of hiding change
Section titled “Transform values instead of hiding change”Persistent collections return an updated value while preserving the original.
(def next-player (assoc player :score 10))
player; => {:name "Nova" :score 0}
next-player; => {:name "Nova" :score 10}This makes the transformation visible. You can inspect the input, the output, and the function between them.
Give behaviour a name
Section titled “Give behaviour a name”A function receives values and returns a value.
(defn add-score [player amount] (update player :score (fn [score] (+ score amount))))
(add-score player 25); => {:name "Nova" :score 25}Read the definition from the outside in:
defncreates a named function.add-scoreis its name.[player amount]lists its inputs.- The remaining form calculates the result.
The function does not need to know where the player came from or how the result will be displayed. That separation lets the same rule be tested, reused, and connected to different hosts.
Make decisions from data
Section titled “Make decisions from data”if selects one of two results:
(def score 12)
(if (>= score 10) "level complete" "keep playing"); => "level complete"Only one branch is evaluated. Change score and run the form again to see the other path.
Use cond when several ordered cases are easier to read:
(defn rank [score] (cond (>= score 100) :gold (>= score 50) :silver :else :bronze))
(rank 70); => :silverA program is often a collection of these small decisions over well-shaped data.
Apply one rule to many values
Section titled “Apply one rule to many values”Use map to transform every item in a collection:
(map (fn [score] (+ score 10)) [0 10 20]); => [10 20 30]The input vector remains available. map returns the new sequence of results.
The same idea scales from three scores to records from a file, rows from a service, drawing commands, or events in a running system.
Make changing state explicit
Section titled “Make changing state explicit”Most values in Hara are immutable. When an interactive program needs a current value that changes over time, place it in an atom.
(def game (atom {:player {:x 10 :y 4} :score 0}))The atom marks the boundary. The map inside it remains an ordinary value.
Define a transition:
(defn move-right [state amount] (update-in state [:player :x] (fn [x] (+ x amount))))Apply it to the current value:
(swap! game move-right 3)
(deref game); => {:player {:x 13 :y 4} :score 0}The current state is visible, the transition has a name, and the result can be inspected before a renderer or host turns it into an effect.
This model works for a game position, selected document, connection status, timer, or any other piece of application state whose changes should remain understandable.
Separate rules from effects
Section titled “Separate rules from effects”A pure function returns a value. An effect crosses into the environment: drawing on a canvas, reading a file, opening a socket, or calling a host object.
Hara keeps that boundary explicit. Application rules can remain ordinary transformations, while a granted capability performs the effect.
input value → application rule → output value → host capability → visible effectYou do not need host access to test the middle of that chain.
Work in a live loop
Section titled “Work in a live loop”Hara is designed to keep the running program close:
write → evaluate → inspect → changeA REPL is not only for isolated calculations. Definitions, atoms, functions, and project state can stay alive while you refine them. When an experiment is correct, keep the definition in durable .hal source.
This gives learning and production work the same basic rhythm. The program becomes more capable without replacing the mental model that got you started.
What you have learned
Section titled “What you have learned”You can now read and write the core of an ordinary Hara program:
- forms call operations;
- vectors hold ordered values;
- maps hold named facts;
- functions transform values;
- conditions choose results;
- persistent collections make updates visible;
- atoms mark changing state;
- capabilities mark effects at the host boundary.
These ideas are enough to begin building something complete.
Continue
Section titled “Continue”Build Tic Tac Toe to combine data, state, rules, pointer input, and rendering from a blank canvas.
Then continue into the Hara language course for persistent collections, streams, coroutines, promises, mutable boundaries, bytes, and files.