100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Your First Clojure Program

Write, structure, and run a complete first Clojure program, covering namespaces, function composition, and program entry points.

FoundationsBeginner10 min readJul 10, 2026
Analogies

Your First Clojure Program

The traditional first Clojure program is a simple (println "Hello, World!") evaluated at the REPL or run as a script. While trivial on its own, successfully running it confirms your JDK, Clojure CLI or Leiningen setup, and basic syntax understanding all work together correctly before you attempt to write anything more substantial.

🏏

Cricket analogy: Writing your first Clojure program, a simple (println "Hello, World!"), is like a young cricketer's first delivery in the nets before ever facing a real match — it's a small, low-stakes action, but successfully executing it confirms your basic mechanics all actually work together before you attempt anything more complex.

Structuring a Namespace

Every non-trivial Clojure source file begins with an ns declaration naming its namespace and requiring the other namespaces it depends on, typically using an :as alias for brevity: (ns my-app.core (:require [clojure.string :as str])). This lets code later refer to a required function as str/upper-case rather than the fully qualified clojure.string/upper-case.

🏏

Cricket analogy: A Clojure namespace declared with (ns my-app.core (:require [clojure.string :as str])) is like a franchise's squad list for a season, naming the team and which specific overseas players it has drafted in, aliased under a shorter squad tag (str) for quick reference on the scoreboard.

Writing and Composing Functions

Idiomatic Clojure programs are built from small, single-purpose functions composed together, often using the -> (thread-first) or ->> (thread-last) macros to make a sequence of transformations read top-to-bottom instead of as deeply nested calls. For example, (-> raw-input parse-command execute-command render-result) threads a value through four functions in a clear, linear order.

🏏

Cricket analogy: Composing small functions together, like feeding the output of (parse-scorecard raw-text) into (calculate-run-rate parsed-data), is like a bowling attack working in tandem — the new-ball bowler sets up the conditions that the first-change bowler then exploits, each doing one clearly defined job that builds on the previous one's output.

Running Your Program

By convention, a Clojure program's entry point is a function named -main, which receives command-line arguments as a sequence of strings. Leiningen projects run it with lein run, while Clojure CLI projects use clj -M -m my-app.core; both approaches load the specified namespace and invoke -main to start execution.

🏏

Cricket analogy: Designating a -main function as the program's entry point is like naming the designated opening batter who walks out first to face the very first ball of the innings — every Clojure program run via lein run or clj -M -m my-app.core starts execution at -main, the same way every innings always officially begins with the openers.

clojure
;; src/my_app/core.clj
(ns my-app.core
  (:require [clojure.string :as str]))

(defn shout [msg]
  (str/upper-case msg))

(defn greet [name]
  (str "Hello, " name "! Welcome to Clojure."))

(defn -main
  [& args]
  (println (greet "World"))
  (println (shout "let's build something")))

;; Run it with:
;;   lein run
;; or:
;;   clj -M -m my-app.core
;;
;; Output:
;; Hello, World! Welcome to Clojure.
;; LET'S BUILD SOMETHING

Namespace names must exactly match their file path and use underscores in the filename where the namespace uses hyphens — a namespace declared as (ns my-app.core) must live in a file at src/my_app/core.clj. Mismatching hyphens and underscores between the namespace and the filename is one of the most common errors beginners hit.

  • A Clojure source file begins with an ns declaration naming its namespace and requiring any needed libraries.
  • The :require clause imports other namespaces, often with a short :as alias for convenience.
  • Small, focused functions can be composed together, often using the -> threading macro, to build larger programs.
  • The -main function is the conventional entry point executed when a program is run.
  • lein run or clj -M -m namespace.name builds and executes a program starting from -main.
  • Namespace names must match their file path exactly, with underscores replacing hyphens in filenames.
  • Structuring code into small, composed functions makes programs easier to test and reason about.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#ClojureStudyNotes#YourFirstClojureProgram#Clojure#Program#Structuring#Namespace#StudyNotes#SkillVeris#ExamPrep