Skip to content

The Hara language course

This course continues from Read Hara and build from scratch. It takes the same small reading model from ordinary values into the runtime boundaries used by complete programs.

No previous Clojure knowledge is assumed. You will work with the actual Hara language and libraries rather than a reduced teaching dialect.

The course develops seven connected parts of Hara:

  1. Basic data types and persistent collections.
  2. Functions and atoms.
  3. Sequences, iterators, and streaming.
  4. Coroutines and promises.
  5. Explicitly mutable arrays and objects.
  6. Bytes and strings.
  7. Capability-gated I/O and files.

The order matters. Each lesson uses the values and boundaries introduced by the earlier lessons.

Hara’s simplicity comes from keeping a small set of distinctions visible as the program grows:

Ordinary modelExplicit boundary
persistent vectors, maps, sets, and listsmutable array and object values
plain immutable valueatom for current changing state
lazy, replayable Seqone-shot iterator
immediate resultpromise that settles later
normal function callresumable coroutine
stringmutable bytes
pure transformationhost capability and I/O

The course does not hide these distinctions behind a framework. It teaches you to recognise and use each one deliberately.

You will build a small line-processing program.

The final program will:

  1. Represent configuration with persistent maps and vectors.
  2. Use functions to transform records.
  3. Store live progress in an atom.
  4. Stream records through sequences and iterators.
  5. Coordinate work with coroutines and promises.
  6. Use arrays and objects at explicit mutable boundaries.
  7. Encode and decode text as bytes.
  8. Read and write files through granted capabilities.

The project is intentionally compact. The goal is to expose the runtime model clearly enough that you can transfer it to a browser application, command-line tool, service, or embedded host.

Keep a Hara REPL or the browser Playground open while you read.

For each example, use the same loop:

  1. Predict the result before running the form.
  2. Run it in the live session.
  3. Change one value or operation.
  4. Inspect the returned value.
  5. Explain the new result in one sentence.

This turns syntax into an observable rule and keeps the running system close to the source.

The course uses one term for each concept:

  • A value is data that an expression returns.
  • A form is Hara data that the evaluator can run.
  • A persistent collection is an immutable collection that returns updated values.
  • A function receives values and returns a value.
  • An atom stores one replaceable value.
  • A Seq is Hara’s lazy, replayable sequence boundary.
  • An iterator is a one-shot source of values.
  • A promise represents a value that can settle later.
  • A coroutine is a resumable computation.
  • An array or object is an explicitly mutable marker value.
  • Bytes are mutable binary storage.
  • I/O is an effect that crosses from Hara into its host environment.

A new name appears only when the course introduces a new runtime concept.

Read numbers, booleans, strings, keywords, symbols, lists, vectors, maps, and sets. Update persistent collections without mutation.

Define transformations with fn and defn. Use an atom when a running program needs one replaceable value.

Build lazy pipelines. Distinguish reusable values, lazy Seq values, and one-shot iterators.

Represent deferred results with promises. Suspend and resume control flow with coroutines.

Use explicit mutable markers at host, protocol, and performance boundaries. Keep them separate from persistent collections.

Transform text with string functions. Encode and decode UTF-8. Inspect and update byte buffers.

Resolve paths safely. Read and write bytes through promises. Understand capability grants and effect boundaries.

Continue according to what you want to build:

Start with 01 — Basic data and persistent collections.