🚀 Clojure Introduction

🎯 Complete Definition

Clojure is a modern, dynamic, functional dialect of Lisp that runs on the Java Virtual Machine (JVM) and also compiles to JavaScript (ClojureScript). Created by Rich Hickey and first released in 2007, Clojure combines the interactive development and code-as-data philosophy of Lisp with the robustness and libraries of the JVM [citation:1]. It emphasizes immutability, persistent data structures, and a pragmatic approach to concurrency, making it ideal for complex, multi-threaded applications [citation:9].

🔬 Core Characteristics

  • Functional: Functions are first-class, data is immutable, and programs are built by applying functions to data [citation:2].
  • Lisp Dialect: Homoiconic (code is data), leveraging macros for powerful metaprogramming [citation:5].
  • Immutable by Default: Core data structures (lists, vectors, maps, sets) are persistent and immutable, ensuring thread-safety [citation:3].
  • Dynamic & Interactive: Developed with a REPL (Read-Eval-Print Loop) for interactive programming and live coding.
  • JVM Hosted: Seamless Java interoperation, allowing use of any Java library [citation:7].
  • Modern Concurrency: Software Transactional Memory (STM), agents, atoms, and core.async for managing state and concurrency [citation:9].
  • Code as Data (Homoiconicity): The syntax is composed of Clojure data structures, enabling macros to transform code in a predictable way [citation:5].

📊 Industry Usage

Clojure is used by companies like Walmart, CircleCI, and Nubank for its stability, performance, and developer productivity. It's popular in finance, big data, and complex web applications. ClojureScript is widely adopted for front-end development, often with frameworks like Reagent and re-frame [citation:6].

;; Clojure 1.11 - CodeOrbitPro Pro Track (println "🍃 Hello, Clojure!") ;; Everything is a list of symbols and data ;; The first element is the function to call (+ 1 2 3 4) ; => 10 (def language "Clojure") (println (str "Learning " language " is fun!")) ;; Interactive development in the REPL (comment ;; This is a comment block for experiments (println "REPL-driven development"))

📊 Syntax & Forms

🎯 Complete Definition

Clojure syntax consists of forms (expressions) that are evaluated to produce values. All code is written as lists of symbols and data. The fundamental unit is the form: numbers, strings, keywords, symbols, lists, vectors, maps, and sets. Lists are interpreted as calls (unless quoted).

🏗️ Basic Forms

  • Numbers: 42, 3.14, 1/3 (ratio), 2N (big int) [citation:3].
  • Strings: "Hello" (Java String).
  • Characters: \a, \newline.
  • Keywords: :keyword (efficient identifiers, often used as map keys) [citation:3].
  • Symbols: my-symbol (name that refers to something else, like a function or var) [citation:3].
  • Booleans: true, false. Also nil represents "no value" and is logically false [citation:3].
  • Comments: ;; for line comments, (comment ...) for blocks.
;; Literal examples 42 ; integer 3.14159 ; double 1/3 ; ratio (accurate fraction) 2N ; big integer "Clojure" ; string \c ; character :keyword ; keyword (evaluates to itself) true ; boolean nil ; null/falsey value ;; List (function call or quoted data) (+ 1 2) ; function call (evaluated) '(1 2 3) ; quoted list (data, not evaluated) ;; Whitespace separates elements, commas are optional/ignored (+ 1, 2, 3) ; same as (+ 1 2 3) ;; Special forms (built-in) (if true "yes" "no") ; conditional (let [x 5] (* x x)) ; local binding

📦 Data Types

🎯 Complete Definition

Clojure data types are rich and leverage Java's type system. It provides scalars (numbers, strings, keywords) and powerful collection types. Numbers include integers, floats, ratios, and arbitrary precision with BigInt (N) and BigDecimal (M) [citation:3].

🔤 Type Details

  • Integers: Java long by default. Arithmetic overflows throw exception; use +', *' for auto-promoting to BigInt [citation:3].
  • Ratio: Exact fraction, e.g., (/ 1 3) yields 1/3.
  • BigInt: Literal 123N.
  • BigDecimal: Literal 3.14159M.
  • Strings: Java String; many Java methods available via interop [citation:3].
  • Keywords: Interned, fast equality, used as functions for map lookup [citation:3].
  • Symbols: Represent identifiers; also usable as functions for map lookup [citation:3].
  • nil: The absence of value; equals Java null.
;; Numeric types (class 42) ; java.lang.Long (class 3.14) ; java.lang.Double (class 1/3) ; clojure.lang.Ratio (class 10000000000000000000N) ; clojure.lang.BigInt (class 3.14159M) ; java.math.BigDecimal ;; Auto-promotion with ' (+ 1 10000000000000000000N) ; BigInt result (+' Long/MAX_VALUE 1) ; 9223372036854775808N (BigInt) ;; Keywords and symbols as functions (def person {:name "Alice" :age 30}) (:name person) ; "Alice" (keyword as function) ('name person) ; nil (symbol not found)

⚙️ Functions

🎯 Complete Definition

Functions are first-class citizens. They are defined with defn (a macro that combines def and fn). Clojure supports arity overloading, variadic functions, anonymous functions with fn or reader literal #(), and closures [citation:2]. Functions automatically return the value of the last expression.

⚙️ Function Features

  • Named: (defn square [x] (* x x)).
  • Multi-arity: Different bodies based on argument count [citation:2].
  • Variadic: & rest collects extra args as a list [citation:2].
  • Anonymous: (fn [x] (* x x)) or #(* % %) [citation:2].
  • Higher-order: Functions that take or return functions (map, filter, reduce).
  • Docstrings: (defn square "Returns x squared" [x] (* x x)).
;; Named function with docstring (defn greet "Says hello to a name." [name] (str "Hello, " name "!")) ;; Multi-arity function (defn messenger ([] (messenger "Hello world!")) ([msg] (println msg))) ;; Variadic function (defn sum [& numbers] (apply + numbers)) (sum 1 2 3 4) ; => 10 ;; Anonymous function (map (fn [x] (* x x)) [1 2 3]) ; => (1 4 9) (map #(* % %) [1 2 3]) ; same, with reader macro ;; Function literals with multiple args (#(+ %1 %2 %3) 1 2 3) ; => 6 (#(println %1 %&) 1 2 3 4) ; prints: 1 (2 3 4) ;; Closures (function capturing surrounding scope) (defn make-adder [x] (fn [y] (+ x y))) (def add5 (make-adder 5)) (add5 3) ; => 8

📋 Collections

🎯 Complete Definition

Clojure collections are immutable, persistent, and support efficient structural sharing. They implement interfaces for reading, and all are counted, iterable, and support seq [citation:3]. The four main types are lists, vectors, maps, and sets [citation:8].

📚 Collection Types

  • List: '(1 2 3) or (list 1 2 3). Singly linked, efficient for prepending (conj at front) [citation:8].
  • Vector: [1 2 3]. Indexed, efficient for appending (conj at end) and random access [citation:8].
  • Map: {:a 1, :b 2}. Key-value pairs, both hash-map and sorted-map [citation:3].
  • Set: #{:a :b :c}. Unordered collection of unique values [citation:3].
;; Lists (def my-list '(1 2 3)) (first my-list) ; 1 (rest my-list) ; (2 3) (conj my-list 0) ; (0 1 2 3) - added at front ;; Vectors (def my-vec [1 2 3]) (get my-vec 1) ; 2 (conj my-vec 4) ; [1 2 3 4] - added at end (peek my-vec) ; 3 (last) (pop my-vec) ; [1 2] (without last) ;; Maps (def my-map {:name "Alice" :age 30}) (get my-map :name) ; "Alice" (:age my-map) ; "Alice" - keyword as function (assoc my-map :city "NYC") ; {:name "Alice", :age 30, :city "NYC"} (dissoc my-map :age) ; {:name "Alice"} ;; Sets (def my-set #{1 2 3}) (conj my-set 4) ; #{1 4 3 2} (contains? my-set 2) ; true

🔀 Control Flow

🎯 Complete Definition

Control flow in Clojure relies on expressions, not statements. Common constructs: if, if-let, when, cond, case, and for (list comprehension). There is no early return; functions always return the last value.

🏗️ Flow Constructs

  • if: (if test then-expr else-expr) (else is optional).
  • when: (when test & body) – like if without else, for side effects.
  • cond: Series of test/expr pairs, like switch.
  • case: Dispatch on constant values (fast).
  • for: List comprehension (not loop, returns lazy seq).
  • do: Groups multiple expressions, returns last.
  • if-let / when-let: Bind and test in one step.
;; if (defn check [x] (if (pos? x) "positive" "non-positive")) ;; when (no else) (defn print-if [x] (when (even? x) (println x "is even"))) ;; cond (defn classify [n] (cond (< n 0) "negative" (zero? n) "zero" :else "positive")) ; :else is truthy ;; case (defn day-name [n] (case n 1 "Monday" 2 "Tuesday" 3 "Wednesday" "unknown")) ;; if-let (defn greet-user [user] (if-let [name (:name user)] (str "Hello, " name) "Hello, anonymous")) ;; for comprehension (lazy) (for [x [1 2 3] y [4 5] :when (odd? (+ x y))] [x y]) ; => ([1 4] [1 6]? wait, this yields pairs)

🔗 Sequences

🎯 Complete Definition

The sequence abstraction is central to Clojure. The seq function returns a sequential view of any collection. Sequences are lazy (values computed only when needed) and can represent infinite streams. Most collection functions (map, filter, reduce) are sequence functions that return lazy seqs [citation:6].

🔧 Key Sequence Functions

  • map: (map inc [1 2 3]) => (2 3 4).
  • filter: (filter even? [1 2 3 4]) => (2 4).
  • reduce: (reduce + [1 2 3 4]) => 10.
  • take/drop: (take 3 (range)) => (0 1 2).
  • into: Converts seq back to collection.
  • ->> and ->: Threading macros for readability [citation:6].
;; Infinite lazy sequence (def naturals (range)) ; all natural numbers (take 5 naturals) ; (0 1 2 3 4) ;; Transformations are lazy (def evens (filter even? naturals)) (take 5 evens) ; (0 2 4 6 8) ;; sum of first 10 odd squares (->> (range) (map #(* % %)) (filter odd?) (take 10) (reduce +)) ; => 1330? Let's compute: squares: 1,9,25,49,81,121,169,225,289,361 sum=1330 ;; map, filter, reduce examples (map inc [1 2 3]) ; (2 3 4) (filter #(> % 2) [1 2 3 4]) ; (3 4) (reduce * [1 2 3 4]) ; 24 ;; into to realize into vector (into [] (map inc [1 2 3])) ; [2 3 4]

📝 Destructuring

🎯 Complete Definition

Destructuring allows you to bind names to parts of a data structure concisely. Works with vectors (positional) and maps (associative). It can be nested and can provide default values [citation:7]. Used in let, function parameters, and for bindings.

🔍 Destructuring Types

  • Sequential (vector): [a b c] binds first three elements.
  • Associative (map): {:keys [name age]} binds name, age from map with keywords; :as whole captures entire map.
  • Rest: [x y & rest] captures remaining.
  • :or: Provides default values.
  • Nesting: Combine forms arbitrarily.
;; Vector destructuring in let (let [[x y z] [1 2 3]] (println x y z)) ; 1 2 3 ;; Rest (let [[first second & rest] [1 2 3 4 5]] (println first second rest)) ; 1 2 (3 4 5) ;; Map destructuring (let [{:keys [name age]} {:name "Alice" :age 30}] (println name "is" age)) ; Alice is 30 ;; Defaults with :or (let [{:keys [name age] :or {age 18}} {:name "Bob"}] (println name age)) ; Bob 18 ;; :as to keep original (let [{:keys [name] :as person} {:name "Charlie" :city "NYC"}] (println name "from" (:city person))) ; Charlie from NYC ;; In function parameters (defn print-person [{:keys [name age]}] (println name "is" age "years old")) (print-person {:name "Dave" :age 40}) ;; Nested destructuring (let [{[x y] :coords} {:coords [10 20]}] [x y]) ; [10 20]

📦 Namespaces

🎯 Complete Definition

Namespaces organize code and avoid name collisions. They are dynamic mappings from symbols to Vars (global variables). Use ns macro to define or switch namespace. Require, use, and import control access to other namespaces and Java classes [citation:7].

📦 Namespace Operations

  • ns: (ns my.project.core (:require [clojure.string :as str]))
  • require: Loads a namespace, optionally with alias.
  • refer: Brings specific symbols into current ns.
  • import: Imports Java classes.
  • create-ns / in-ns: Lower-level functions.
  • alias: Create alias for a namespace.
(ns user (:require [clojure.string :as str] [clojure.set :as set]) (:import java.util.Date)) ;; Use aliased functions (str/upper-case "hello") ; "HELLO" ;; Java interop with imported class (Date.) ; current date (calls constructor) ;; Define a var in current namespace (def my-var 42) ;; Create a new namespace programmatically (in-ns 'my.temp) (def temp-var 100) ;; Back to user (in-ns 'user) ;; my.temp/temp-var not accessible here without require ;; Reloading namespaces (development) (require 'my.temp :reload)

🔄 State & Identity

🎯 Complete Definition

State management in Clojure separates identity from state. Identities (references) are mutable references to immutable values. Over time, an identity can point to different immutable states. Clojure provides three reference types for managing changing state: Atoms, Refs, and Agents, each with different coordination semantics [citation:9].

🔄 Reference Types

  • Atoms: Synchronous, independent, uncoordinated updates. Use swap! (apply function) or reset! [citation:4].
  • Refs: Synchronous, coordinated updates via Software Transactional Memory (STM) with dosync, alter, ref-set [citation:9].
  • Agents: Asynchronous, independent updates. Send actions with send (pooled threads) or send-off.
  • Vars: Per-thread bindings with binding [citation:9].
;; Atoms - independent synchronous state (def counter (atom 0)) ;; swap! applies a function to current value (swap! counter inc) ; => 1 (swap! counter + 5) ; => 6 ;; deref with @ to read @counter ; => 6 ;; Refs - coordinated changes (STM) (def account-a (ref 100)) (def account-b (ref 200)) (defn transfer [from to amount] (dosync (alter from - amount) (alter to + amount))) (transfer account-a account-b 50) @account-a ; 50 @account-b ; 250 ;; Agents - asynchronous updates (def logger (agent [])) ;; send queues action to thread pool (send logger conj "Log entry 1") (send logger conj "Log entry 2") ;; wait for all sends to complete (for demonstration) (await logger) @logger ; ["Log entry 1" "Log entry 2"]

⚡ Concurrency

🎯 Complete Definition

Concurrency in Clojure is built on immutable data and managed references, making multi-threaded programming safer. In addition to STM, atoms, and agents, Clojure offers core.async for CSP-style concurrency with channels and go blocks [citation:7]. Futures and promises simplify async tasks.

⚡ Concurrency Primitives

  • Future: (future (long-running-task)) – runs in another thread, returns a deref-able object.
  • Promise: (def p (promise)) (deliver p value) – one-time delivery.
  • pmap: Parallel version of map.
  • core.async: Channels, go blocks, , >!! for async communication.
;; Futures (def f (future (Thread/sleep 2000) "done")) (future-done? f) ; false (after 2s true) @f ; "done" (blocks until ready) ;; Promises (def p (promise)) (future (println "Waiting for value...") (println "Got:" @p)) (deliver p 42) ; unblocks the future ;; pmap - parallel map (defn expensive [x] (Thread/sleep 100) (* x x)) (time (doall (map expensive (range 10)))) ; ~1000ms (time (doall (pmap expensive (range 10)))) ; ~100ms (parallel) ;; core.async example (requires [clojure.core.async :refer [go chan >! ! ch "hello")) (go (println "Received:" (

🔮 Macros

🎯 Complete Definition

Macros are compile-time functions that transform code. Because Clojure is homoiconic (code is data), macros receive unevaluated forms and return new forms to compile. They enable custom syntactic abstractions without changing the language [citation:5]. Use macros when functions cannot (e.g., need to control evaluation, create new binding forms).

🔮 Macro Essentials

  • defmacro: Defines a macro.
  • Syntax quote (`): Quotes a form, but allows unquoting (~) and unquote-splicing (~@).
  • Unquote (~): Inside syntax quote, evaluates the expression.
  • Unquote-splicing (~@): Inserts all elements of a list.
  • gensym: Generates unique symbols to avoid naming conflicts.
;; Simple macro: infix notation (defmacro infix "Translate infix to Clojure form" [form] (list (second form) (first form) (last form))) (infix (6 + 6)) ; expands to (+ 6 6) => 12 ;; Using syntax quote (defmacro unless [test then] `(if (not ~test) ~then)) (unless false (println "this prints")) ; prints ;; Macro with unquote-splicing (defmacro with-logging [& body] `(do (println "Starting...") ~@body (println "Finished."))) (with-logging (println "Hello") (println "World")) ;; Hygiene: avoid variable capture (defmacro safe-let [bindings & body] (let [names (take-nth 2 bindings)] `(let ~bindings ~@body))) ; but if body uses a local named 'names', conflict ;; Better: gensym (defmacro my-let [bindings & body] (let [names (take-nth 2 bindings) gnames (map (fn [_] (gensym "name_")) names)] ;; ... more complex hygiene ))

☕ Java Interop

🎯 Complete Definition

Java interoperability is seamless in Clojure. You can create objects, call methods, access fields, extend classes, and implement interfaces using concise syntax. This gives Clojure access to the entire Java ecosystem [citation:7].

☕ Interop Syntax

  • Instance creation: (java.util.Date.) – dot after class name.
  • Instance method: (.toUpperCase "hello") or (. "hello" toUpperCase).
  • Static method: (Math/sqrt 25) – slash.
  • Static field: Math/PI.
  • Instance field: (.-x point) – dot dash.
  • Inner class: java.util.Map$Entry.
  • Implementing interfaces: proxy or reify [citation:7].
;; Creating objects (def now (java.util.Date.)) (def list (java.util.ArrayList.)) ;; Calling methods (.add list "item1") (.get list 0) ; "item1" ;; Shorthand: .. for chaining (.. System getProperties (get "java.version")) ; chain calls ;; Static methods and fields (Math/sqrt 16) ; 4.0 Math/PI ; 3.141592653589793 ;; Accessing fields (.-x (java.awt.Point. 10 20)) ; 10 ;; Importing to avoid fully qualified names (ns example (:import [java.util ArrayList Date] [java.net URL])) (ArrayList.) ; now works ;; reify - create anonymous implementation of interface/class (def runnable (reify Runnable (run [this] (println "Running in thread")))) (.start (Thread. runnable)) ;; proxy - more dynamic (for classes with constructors) (def handler (proxy [java.awt.event.ActionListener] [] (actionPerformed [e] (println "Action:" e))))

🚀 Advanced Features

🎯 Complete Definition

Advanced Clojure includes features like protocols, records, transducers, reducers, and spec. These enable high-performance abstraction, data validation, and generation [citation:7].

🚀 Advanced Topics

  • Protocols: High-performance polymorphism (like interfaces) [citation:7].
  • Records: Map-like with fixed keys, faster access [citation:3].
  • Transducers: Composable algorithmic transformations independent of context (can be used with collections, channels, etc.) [citation:1].
  • Spec: Specification for data and functions, with validation and generative testing.
  • Reducers: Parallel fold operations on collections [citation:1].
  • core.logic: Logic programming [citation:1].
  • core.async: CSP channels.
;; Protocol definition (defprotocol Greet (greet [this] "Say greeting")) ;; Extend via record (defrecord Person [name] Greet (greet [this] (str "Hello, I'm " name))) (->Person "Alice") (greet *1) ; "Hello, I'm Alice" ;; Transducers (independent of input source) (def xform (comp (map inc) (filter even?))) (into [] xform [1 2 3 4]) ; [2 4] ;; can be used with channels, sequences, etc. ;; clojure.spec (validation) (require '[clojure.spec.alpha :as s]) (s/def ::name string?) (s/def ::age pos-int?) (s/def ::person (s/keys :req [::name ::age])) (s/valid? ::person {::name "Bob" ::age 30}) ; true ;; core.async (CSP) (require '[clojure.core.async :as async]) (def ch (async/chan)) (async/go (async/>! ch "hi")) (async/go (println "got:" (async/