
In Clojure, everything is an expression wrapped in parentheses, and that single idea unlocks the entire language. Unlike languages where you juggle semicolons, curly braces, and operator precedence, Clojure keeps it refreshingly simple: open a parenthesis, name the operation, list the arguments, close the parenthesis. You will learn how Clojure evaluates prefix notation expressions like (+ 1 2) and (str "hello" " " "world"), how nested expressions are resolved from the inside out, and why this uniform structure makes Clojure code surprisingly easy to reason about once the initial surprise of all those parentheses wears off.
Every programming language has its basic building blocks, and in Clojure those primitives are clean, straightforward, and ready to use right out of the box. You will explore how Clojure handles integers, floating-point numbers, ratios like 22/7 that stay exact instead of rounding, strings enclosed in double quotes, and the boolean values true and false along with nil. This lecture walks you through writing Clojure expressions that use arithmetic operators on numbers, the str function to concatenate strings, and comparison operators to produce boolean results, giving you a solid grip on the data types you will use in every Clojure program you write.
Giving names to things is one of the most fundamental acts in programming, and in Clojure you do it with def. This lecture shows you how to bind a name to a value using expressions like (def greeting "Hello, Clojure!") and then reference that name later in your code. You will see how def creates a var in the current namespace, understand that Clojure values are immutable by default so a def is more like labeling a jar than filling a mutable box, and practice defining and using several named values including numbers, strings, and the results of computed expressions.
Clojure ships with a rich standard library of functions, and knowing how to call them is your bread and butter. In this lecture you will practice calling a variety of built-in Clojure functions such as inc, dec, max, min, count, subs, and type, learning the consistent pattern of (function-name argument1 argument2). You will see how Clojure functions always return a value, how you can nest function calls like (inc (max 3 7 2)) to compose behavior, and how the uniform prefix syntax means there is never any ambiguity about what operation is being performed or in what order.
Good code communicates intent, and Clojure gives you several ways to annotate your work. In this lecture you will learn how to write single-line comments using the semicolon, how to use the comment macro to wrap blocks of code that should be ignored during evaluation, and how to use the #_ reader macro to discard the next form entirely. You will practice each style in Clojure code snippets and understand when each approach is most useful — from quick notes explaining a tricky expression to temporarily disabling a chunk of code while you experiment.
When you need an ordered collection of items in Clojure, vectors are your go-to choice. Written with square brackets like [1 2 3 "four" true], vectors let you store any mix of values and access elements by index using the get function or by calling the vector itself as a function. In this lecture you will create vectors, retrieve elements with (get my-vec 0) and (my-vec 2), add elements to the end with conj, check the count, and see how Clojure vectors are immutable — every operation returns a new vector while the original stays untouched, like editing a copy of a document rather than the original.
Lists are the oldest data structure in the Lisp family, and Clojure inherits them with pride. Because Clojure code itself is made of lists, you need the quote operator or the list function to create a data list without it being evaluated as a function call. This lecture teaches you how to build lists in Clojure using '(1 2 3) and (list "a" "b" "c"), how conj adds elements to the front of a list rather than the end, and how to retrieve the first element with first and the rest with rest. You will understand when lists are preferable to vectors and appreciate the elegant symmetry between Clojure code and Clojure data.
Maps are arguably the most important data structure in Clojure, used everywhere from configuration to representing entities. Written with curly braces like {:name "Frodo" :age 50 :race "Hobbit"}, a Clojure map associates keys with values and gives you lightning-fast lookups. You will learn how to create maps, retrieve values with get and keyword access like (:name my-map), add or update entries with assoc, remove entries with dissoc, and check for keys with contains?. This lecture demonstrates how Clojure maps stay immutable through every transformation, making your data predictable and your debugging life far easier.
Sometimes you need a collection where every element appears exactly once, and in Clojure that is a set. Written with the #{} syntax like #{:red :green :blue}, sets automatically eliminate duplicates and let you check membership instantly. In this lecture you will create Clojure sets, add elements with conj, remove them with disj, test membership by calling the set as a function or using contains?, and perform set operations like union, intersection, and difference from the clojure.set namespace. Sets are perfect for modeling categories, tags, permissions, and anything else where uniqueness matters.
Real-world data is rarely flat, and Clojure handles nested structures with elegance. In this lecture you will build Clojure data structures that nest maps inside vectors, vectors inside maps, and combinations of all collection types. You will use get-in to reach deep into nested data like (get-in person [:address :city]), update nested values with assoc-in, and modify them with update-in. These three functions form a powerful trio that lets you surgically read and transform any part of a complex data tree while keeping everything immutable, which is one of the superpowers that makes Clojure such a joy for data manipulation.
Creating your own functions in Clojure is where the language truly comes alive. Using defn, you define a named function with a parameter list and a body, like (defn greet [name] (str "Hello, " name "!")). This lecture walks you through writing Clojure functions that accept arguments and return values, understanding that the last expression in the body is automatically the return value with no return keyword needed. You will practice defining functions that perform calculations, format strings, and operate on collections, building your confidence in packaging reusable logic into clean, named units.
Not every function needs a name, and Clojure makes it easy to create throwaway functions on the fly. In this lecture you will learn two ways to write anonymous functions in Clojure: the full form with fn like (fn [x] (* x x)) and the compact shorthand #(* % %) that saves keystrokes when the logic is simple. You will see how anonymous functions are commonly passed as arguments to other functions, how the percent signs %1, %2 work as parameter placeholders in the shorthand form, and when to choose each style for maximum readability.
Making decisions is essential in any program, and Clojure provides several clean ways to branch your logic. In this lecture you will learn how Clojure's if expression evaluates a condition and returns one of two values like (if (> temp 30) "hot" "comfortable"), how when is a streamlined version for cases where you only care about the truthy branch, and how cond lets you chain multiple conditions like a series of elif checks. You will also discover that in Clojure only false and nil are falsy — everything else, including zero and empty strings, is truthy, which is a key difference from many other languages.
Sometimes you need temporary names inside a function, and Clojure's let expression gives you exactly that. With let, you create local bindings that exist only within a specific scope, like (let [radius 5 area (* Math/PI radius radius)] area). This lecture shows you how to write Clojure let blocks that bind one or more values, how later bindings can reference earlier ones in the same block, and how let keeps your functions tidy by avoiding repeated computation. You will practice using let to break complex calculations into readable, named steps without polluting the global namespace.
Clojure's destructuring lets you pull apart data structures right where you bind names, saving you from repetitive get calls. In this lecture you will learn how to destructure vectors in Clojure function parameters like (defn greet [[first-name last-name]] ...) to grab elements by position, and how to destructure maps using (defn process [{:keys [name age]}] ...) to extract values by key. You will also see how to provide default values with :or and keep access to the original structure with :as, making your Clojure functions both concise and expressive when they receive complex data.
Clojure functions can be remarkably flexible about how many arguments they accept. In this lecture you will learn how to define multi-arity functions in Clojure that behave differently depending on the number of arguments, like a greet function that says "Hello, stranger!" with zero arguments and "Hello, Dana!" with one. You will also learn how to write variadic functions using the ampersand syntax (defn sum [& numbers] ...) that accept any number of arguments collected into a sequence. These techniques let you write Clojure functions with sensible defaults and open-ended inputs without resorting to complex overloading mechanisms.
The map function is your Swiss Army knife for transforming collections in Clojure. It takes a function and a collection, applies the function to every element, and returns a new sequence of results. In this lecture you will write Clojure expressions like (map inc [1 2 3]) to add one to every number, (map :name users) to extract a field from each map in a vector, and (map + [1 2 3] [10 20 30]) to combine multiple collections element by element. You will see how map works lazily, producing results on demand, and how it frees you from writing manual loops.
Selecting the right data from a collection is a task you will face constantly, and Clojure's filter and remove functions make it effortless. In this lecture you will use filter with a predicate function to keep only the elements that satisfy a condition, like (filter even? [1 2 3 4 5 6]) to get the even numbers. You will also learn how remove is the complement of filter, discarding elements that match the predicate, and how to use anonymous functions as predicates for custom criteria in Clojure. Together, these functions let you slice and dice data without mutating anything.
When you need to boil a collection down to one result — a sum, a maximum, a merged map — reduce is the Clojure function for the job. In this lecture you will learn how (reduce + [1 2 3 4 5]) accumulates a running total, how to provide an initial value with (reduce conj [] some-set) to control the output type, and how the reducing function receives an accumulator and each element in turn. You will practice writing Clojure reduce expressions that compute totals, build new collections, and aggregate data, unlocking one of functional programming's most versatile patterns.
Deeply nested function calls can become hard to read, and Clojure's threading macros solve this beautifully. The thread-first macro -> passes a value as the first argument through a pipeline of functions, while thread-last ->> passes it as the last argument. In this lecture you will transform a tangled Clojure expression like (reduce + (filter even? (map inc (range 10)))) into a clean, top-to-bottom pipeline with (->> (range 10) (map inc) (filter even?) (reduce +)). You will practice both -> and ->> and learn which to use depending on whether you are working with single values or collections.
Clojure embraces laziness as a feature, not a bug — many sequence operations produce results only when they are actually needed. In this lecture you will explore how Clojure's range function generates potentially infinite sequences of numbers, how take and drop let you grab slices from lazy sequences without evaluating the whole thing, and how functions like map and filter produce lazy sequences themselves. You will experiment with expressions like (take 5 (filter odd? (range))) to pluck five odd numbers from an infinite stream, understanding how Clojure's laziness lets you describe computations over unbounded data without running out of memory.
Beyond the big three of map, filter, and reduce, Clojure's standard library is packed with handy sequence functions. In this lecture you will learn how apply unpacks a collection into individual arguments so (apply max [3 7 1 9]) works like (max 3 7 1 9), how partition splits a sequence into groups of a fixed size, and how frequencies counts occurrences to produce a map like {"a" 3, "b" 1}. You will also explore take-while and drop-while for grabbing elements until a condition changes, giving you more precise control over how you process sequences in Clojure.
Strings show up everywhere in real programs, and Clojure gives you a solid set of tools to manipulate them. In this lecture you will explore the clojure.string namespace, learning functions like clojure.string/upper-case, clojure.string/lower-case, clojure.string/trim, clojure.string/split, clojure.string/join, and clojure.string/replace. You will require the namespace and use these functions in Clojure expressions to transform text, split sentences into words, join collections into comma-separated strings, and perform search-and-replace operations, all while appreciating how Clojure strings are standard Java strings with a functional interface on top.
Clojure defaults to immutability, but sometimes you genuinely need mutable state, and atoms are the controlled, thread-safe way to get it. In this lecture you will create Clojure atoms with (def counter (atom 0)), read their current value with @counter or (deref counter), update them with swap! and a function like (swap! counter inc), and reset them with reset!. You will see how atoms enforce a discipline where changes happen through pure functions applied to the current state, making even mutable state in Clojure safer and more predictable than traditional variable mutation.
Functions that create new functions from existing ones are a hallmark of elegant Clojure code. In this lecture you will learn how comp composes multiple functions into one, so (def shout (comp clojure.string/upper-case clojure.string/trim)) creates a function that trims and uppercases in a single call. You will also learn how partial fixes some arguments of a function to create a specialized version, like (def add-ten (partial + 10)). These higher-order tools in Clojure let you build complex behavior by snapping simple functions together like Lego bricks, keeping your code concise and reusable.
Things go wrong in every program, and Clojure provides try-catch for handling exceptions gracefully. In this lecture you will write Clojure try expressions that attempt a risky operation and catch specific exception types, like (try (/ 10 0) (catch ArithmeticException e "Cannot divide by zero")). You will learn how to access the exception message with (.getMessage e), use finally for cleanup code that runs regardless of success or failure, and throw your own exceptions with (throw (ex-info "Something broke" {:code 42})). Understanding error handling in Clojure ensures your programs fail gracefully instead of crashing mysteriously.
While Clojure encourages you to think in terms of sequence operations, sometimes an explicit loop is the clearest way to express an algorithm. In this lecture you will learn how Clojure's loop and recur provide a way to write iterative code without consuming stack frames, using expressions like (loop [n 5 result 1] (if (zero? n) result (recur (dec n) (* result n)))) to compute a factorial. You will understand that recur is Clojure's mechanism for tail-call optimization, see how it works both inside loop and inside defn, and practice writing iterations that accumulate results safely without stack overflow concerns.
Clojure's multimethods let you define functions that behave differently based on the value of their arguments, giving you polymorphism without classes. In this lecture you will use defmulti to declare a Clojure multimethod with a dispatch function, then use defmethod to provide implementations for different dispatch values. For example, you might define (defmulti area :shape) and then write separate defmethod implementations for :circle, :rectangle, and :triangle, each computing the area with its own formula. Multimethods in Clojure are flexible enough to dispatch on any computed value, making them more powerful than traditional type-based polymorphism.
This course contains the use of Artificial Intelligence (AI).
Functional programming is no longer a niche academic pursuit — it is powering some of the most resilient, scalable systems in production today, and Clojure sits at the sweet spot of practicality and elegance. Whether you have been curious about Lisps, intrigued by immutability, or simply looking for a language that makes concurrent programming less terrifying, Clojure offers a refreshingly different way to think about code. This course gives you a hands-on, code-first foundation in a language that has earned a devoted following among developers who value simplicity, power, and joy in their daily work.
You will start with the absolute basics — how Clojure expressions work, what prefix notation means, and how to define values and call functions. From there, you will dive into Clojure's legendary immutable data structures: vectors, lists, maps, and sets, learning how to create, access, nest, and transform them without ever mutating data in place. The course then builds your ability to write your own functions, use conditionals, work with local bindings, and leverage destructuring to write clean, expressive code. A full section on sequence processing teaches you to wield map, filter, reduce, and threading macros to process data in elegant pipelines. Finally, you will tackle real-world patterns including string manipulation, state management with atoms, function composition, error handling, explicit iteration with loop and recur, and polymorphic dispatch with multimethods.
This course is designed for programmers who have some experience in another language and want to learn Clojure from the ground up. You do not need prior experience with functional programming or any Lisp dialect. By the end, you will be able to read and write idiomatic Clojure code, work confidently with immutable data structures, compose functions into data-processing pipelines, and apply patterns used in production Clojure codebases. Every lecture focuses on a single concept demonstrated through runnable code, so you build understanding incrementally and can practice as you go.
What sets this course apart is its relentless focus on code you can actually run and experiment with — no slides full of theory without practice, no meandering discussions, just clear explanations paired with Clojure expressions you can type and evaluate immediately. If you are ready to add one of the most elegant and productive languages in the JVM ecosystem to your toolkit, enroll now and start writing Clojure today.