Clojure Crash Course for JavaScript Developers

Apr 15 2019 Sep 3 2019

Welcome to the first post in my series detailing an implementation of Wave Function Collapse.

Before we start writing any code, we must talk about the programming language we will be using — Clojure. I assume most people following along are not familiar with it, so I will teach you enough to read and follow along with later articles.

Many of you may only have experience with a single programming language, and the idea of learning another may seem daunting. Don’t worry. Learning your second language is much easier than the first, even though Clojure may seem alien.

In fact, many of the concepts integral to Clojure have made their way into JavaScript. Things like anonymous functions, map/filter/reduce and immutability will be familiar to you.

If you would like to follow along, both repl.it and clojurescript.io have repls to get you started.

Basic Syntax

In JavaScript, we invoke functions the way we expect from math class:

function inc(x) {
    return x + 1;
}

inc(1);
2

Let’s do the same thing in Clojure

(inc 1)
2

The parenthesis go on the outside. In Clojure, instead of invoking functions with f(x), we do (f x). While initially confusing, it offers distinct advantages. One is that our code is a list. This makes it data. We can edit, process and modify our code using the same tools we use to work with data. We won’t get into that here, but it is very cool.

Second, more practically, is that it is almost all of the syntax you need to know. With the exception of data literals (like vectors), and some common macros, that is pretty much it.

That includes math:

In JavaScript

1 + 1
2

In Clojure

(+ 1 1)
2

Notice how there are no commas between arguments. In Clojure, we use whitespace to separate arguments.

It is weird, right? But this lets us do things like this:

(+ 1 2 3 4 5 6)
21

That discomfort you may feel reading it compared to JavaScript goes away quickly, especially why you realize you don’t have to constantly switch between normal function, infix operators (like +), and member functions.

Functions

In JavaScript, we define functions like so

function add(x, y) {
    return x + y
}

add(1, 2);
3

In Clojure

(defn add [x y]
  (+ x y))

(add 1 2)
3

Here is the basic syntax:

(defn function-name [argument1 argument2 ...]
  (body))

We use a macro called defun and specify a function name. Note that we can, and prefer, to use hyphenated (or kebabed) names for our functions. We get to do this because minus, -, is a function in Clojure, not a special symbol that must be reserved.

Then, arguments are specified using a vector. Note the lack of commas.

Finally, the function body is defined. There is no explicit return statement because the body’s value is automatically returned.

Anonymous Functions

In JavaScript, we have two syntaxes: the function operator and the arrow function.

function(x) {
    return x + 1;
}

(x) => {
    return x + 1;
}

In Clojure we also have two syntaxes: the function expression and the reader macro.

(fn [x]
  (+ x 1))

#(+ % 1)

The first should be familiar and reasonable. Instead of using defn, we write fn with no function name.

The second is more cryptic. The reader macro is transformed by the Clojure parser and into the first form. It is indicated by a hashed parenthesis #(). Inside, the % is evaluated to the first argument. We can use multiple arguments by adding an integer.

;; The same as the add function
#(+ %1 %2)
(#(+ %1 %2) 6 8)
14

Special Forms and Flow Control

What if we want to do multiple things inside of our function body? There is always the do macro. This gives us an environment similar to JavaScript, where each line is executed independently. The value of the last expression executed is returned.

(do
  (println "Hello, World!")
  (println 2)
  (+ 1 2))
Hello, World!
2
3

As you can see, println is our equivalent to console.log.

Conditional logic is also important. In Clojure, if is an expression. It evaluates a statement, and then if it is true or false it executes the appropriate branch and returns its value.

(if true
  "yes" ;; true branch
  "no") ;; false branch
"yes"

We can use this returned value inside of other expressions

(+ 2 (if false 3 4))
6

This is very similar to the ternary operator in JavaScript

2 + (false ? 3 : 4)
6

Same effect, but look at how much simpler the Clojure syntax is.

If you want to chain together if’s and else’s like in JavaScript, use cond instead.

We also have access to our same toolkit of logical operators, but in Clojure they are functions instead of symbols.

(println (and true false)
         (or true false)
         (not true)
         (= 1 2)
         (= 1 1))
false
true
false
false
true

You may be weirded out to see the equals sign used to test equality instead of assigning values.

Variables

In Clojure, we handle variables a bit differently. We tend to work with our data by passing it from one function to the next, instead of storing it in a variable and mutating it.

However, we still want to be able to associate constant values with names, and to use the results of a computation in multiple places.

In the tradition we are more familiar with, Clojure has the def macro.

(def a 1)
(println a)
(println (+ a 2))
1
3

It defines a variable to a name. It is roughly equivalent to const in the global scope in JavaScript. We do not use def inside functions to make local variables.

Instead, the let macro is used.

(let [a 1
      b (+ a 2)]
  (+ a b))
4

Here is the basic syntax:

(let [variable1 expression1
      variable2 expression2
      ...]
  (body))

It takes a vector of variable names and expressions. The expressions are evaluated and the variables are bound to their value inside of the let’s scope. We can reference earlier variables within the expressions for later variables, as well as in the body.

Again, they are constant and we do not mutate their values.

Data Structures

Numbers

Like JavaScript we have integer and decimal (floating point) numbers. We also have fractional numbers.

Math is straightforward, with the expected symbols defined as function.

1
2.5
(+ 3 4.6)  ; -> 7.6
(/ 2 4)    ; -> 1/2
(quot 2 4) ; 0
(mod 2 4)  ; 2
(2 / 4.0)  ; -> 0.5
(inc 2)    ; -> 3
(dec 3)    ; -> 2

Strings

Strings are also much as we expect, but unlike JavaScript they are always enclosed by double quotes, or created with the str function.

"I'm a string"
(str "test" "other")
"testother"

Also unlike JavaScript, things like addition are not overloaded to handle strings.

(+ "1" "b")
class java.lang.ClassCastExceptionclass java.lang.ClassCastExceptionExecution error (ClassCastException) at user/eval7088 (REPL:2).
java.lang.String cannot be cast to java.lang.Number

Instead, functions to work with strings are found in the clojure.string namespace. A further discussion of namespaces is needed, but for now we access their functions and variables by stating a namespace followed by a slash and the name of what we want to access.

(println
 (clojure.string/trim "     trim me    ")
 (clojure.string/lower-case "ABC")
 (clojure.string/reverse "olleh")
 (clojure.string/join ", " ["a" "b" "c"]))
"trim me"
"abc"
"hello"
"a, b, c"

Keywords

:a
:i-am-a-keyword
(keyword "test") ; :test

Keywords are new. They are identifiers and you can think of them as fast, constant strings. Keywords are commonly used as keys in maps. They’re great and you will see why later.

Vectors

We have already seen vectors in a few places, such as defining function arguments. Like arrays in JavaScript, they are enclosed by square brackets. They do not need commas by default, but they can be used.

[1 2 3]
["a", 4, (fn [x] (+ x 2))]
(vector 5 6 7)

There are a host of functions for working with them. Here is a sample:

;; index access
(nth [1 2 3] 0)   ; 1
(get [1 2 3] 0)   ; 1
;; conventiently get the first few elements
(first [1 2 3])   ; 1
(second [1 2 3])  ; 2
(rest [1 2 3])    ; [2 3]
;; add an element to the end
(conj [1 2 3] 4)
;; remove an element from the end
(pop [1 2 3])     ; [1 2]
;; get last element
(peek [1 2 3])    ; 3
;; replace element at index
(assoc ["a" "c" "c"] 1 "b") ; ["a" "b" "c"]
;; create a vector
(vector 1 2 3)    ; [1 2 3]

Note that all of these operations are immutable. For example:

(def test-vector [1 2 3])
(println (conj test-vector 4))
(println test-vector)
[1 2 3 4]
[1 2 3]

Modifying a vector, or any of our basic data structures, creates a new copy and does not change the old value or any variables associated with it. Your first thought might be that it sounds slow, wasteful or inconvenient.

Immutable data structures are fast and wonderful. They are fast because they are implemented using persistent data structures, which is to say instead of creating an entirely new vector with every operation , Clojure creates a vector that overlaps with the original and only stores the newly created or modified elements.

While that does introduce some overhead it makes data incredibly convenient to reason about. We never have to worry about a function changing data. We get what is returned. Nothing more, nothing less. If you have worked with modern JavaScript frameworks, you may realize how annoying unexpected mutation can be and how frequently you visit doesitmutate.xyz.

Lists

We also have lists. They are like our code, but with a ' in front indicating it is a literal value.

'(1 2 3)
(list 1 2 3) ; '(1 2 3)

In fact, it is our code

(eval '(inc 1))
2

They are similar to vectors, but each element is internally linked to the next. We have to traverse the list to access elements, but it is very fast to add items to the front. They are also lazy, which we will explain later.

;; index access (works, but is slow)
(nth '(1 2 3) 2)   ; 3
;; get doesn't work, because it is technically a key lookup
(get '(1 2 3) 0)   ; nil
;; conventiently get the first few elements
(first '(1 2 3))   ; 1
(second '(1 2 3))  ; 2
(rest '(1 2 3))    ; '(2 3)
;; add an element to the front
(conj '(2 3 4) 1)  ; '(1 2 3 4)
(cons 1 '(2 3 4))  ; '(1 2 3 4)
;; remove an element from the front
(pop '(1 2 3))     ; '(2 3)
;; get last element
(last '(1 2 3))    ; 3

If you are paying close attention you may have noticed something strange. conj and pop add and removes items from the beginning of a list, but add and remove items from the end of a vector.

This is because it is efficient to add and remove items from the beginning of a list, and efficient to add and remove items at the end of a vector.

The efficient option is performed by default, and adding items to the end of a list or the beginning of a vector is made deliberately difficult because it means you should either pick another data structure or be aware of the detriment to performance.

Maps

Maps are like Objects, but they lack prototype functions. Unlike JavaScript, we can use any data type as the key. We can also use commas if we like to make them easier to read.

{:a 1 :b 2}
{:a 1, :b 2}
{1 2, 3 4}
(hash-map :a 1 :b 2)

We commonly use keywords as keys in maps because the are fast, and we can also use them as functions to access their value

(:b {:a 1 :b 2})
2

You can think of this as a Clojure equivalent to Object.property syntax in JavaScript. Note that it does not work with other types:

("b" {"a" 1 "b" 2})
class java.lang.ClassCastExceptionclass java.lang.ClassCastExceptionExecution error (ClassCastException) at user/eval7164 (REPL:2).
java.lang.String cannot be cast to clojure.lang.IFn

The functions that operate on them have significant overlap with vectors as maps and vectors are implemented very similarly.

;; get value
(get {:a 1 :b 2} :a)             ; 1
(:a {:a 1 :b 2})                 ; 1
(get {"a" 1 "b" 2} "b")          ; 2
;; add or change a value for a given key
(assoc {:a 1 :b 2} :c 3)         ; {:a 1 :b 2 :c 3}
(assoc {:a 1 :b 2} :b 3)         ; {:a 1 :b 3}
;; add a pair
(conj {:a 1 :b 2} [:c 3])        ; {:a 1 :b 2 :c 3}
;; merge maps
(merge {:a 1 :b 2} {:b 3 :c 4})  ; {:a 1 :b 3 :c 4}
;; remove a key and value
(dissoc {:a 1 :b 2} :b)          ; {:a 1}
;; get a subset of the map
(select-keys {:a 1 :b 2 :c 3} [:a :b]) ; {:a 1 :b 2}

More

Clojure has more built in data structures, like sets, sorted-sets, sorted-maps and a few more.

Clojure also has a very large standard library full of incredibly useful, specific and well named functions for working with and creating data. You can find them all on ClojureDocs. We will use some of them in the concluding example problems.

Sequence functions and Laziness

One of the best things to happen to JavaScript was the addition of the sequence functions map, filter and reduce. Map applies an operation each element of a sequence and returns the resulting sequence. Filter performs a test on each element and keeps those that pass. Reduce takes an accumulator value and folds each element into that accumulator.

In Clojure, they all have the same syntax:

(map function collection)
(filter function collection)
(reduce function collection) ;; function is of two variables

In JavaScript, if we wanted to double each number of an array:

[1, 2, 3].map(x => x * 2)
[2,4,6]

In Clojure the following are equivalent:

(map (fn [x] (* x 2)) [1 2 3])
(map #(* % 2) [1 2 3])
(2 4 6)

We can use a reducer to sum an array. In JavaScript:

[1, 2, 3].reduce((acc, x) => acc + x)
6

In Clojure:

(reduce (fn [acc x] (+ acc x)) [1 2 3])
(reduce #(+ %1 %2) [1 2 3])
(reduce + [1 2 3])
6

You can also see why it is an advantage to have + be a function.

We could use a filter to remove odd elements. In JavaScript:

[1, 2, 3, 4].filter(x => x % 2 === 0)
[2,4]

In Clojure:

(filter (fn [x] (even? x)) [1 2 3 4])
(filter #(even? %) [1 2 3 4])
(filter even? [1 2 3 4])
(2 4)

If you have a sharp eye, you may have noticed that map and filter returned lists even though we gave them vectors. Herein lies a powerful difference with the Clojure functions — they are lazy.

What this means is that these functions do not actually execute on each element of a sequence until that value is needed.

For example, the take function will take the first n elements from a sequence. The range function will produce a list of numbers from 0 to n. If we map over a collection, but only take the first 3, then those three elements are the only ones whose values are calculated.

(take 3 (map #(* % 2) (range 1000000000)))
(2 4 6)

Don’t believe me? Let’s check:

(take 3 (map #(or (println %) (* % 2)) (range 1000000000)))
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
(0 2 4)

It turns out that for most cases it is actually more efficient to calculate them in chunks of 32 elements. So it is not a silver bullet for avoiding needless computation, but it does help and it does let us do some really cool things. Like the fact that range, when given no argument, produces an infinite sequence of numbers. You read that right. Laziness allows us to produce infinite sequences and only consume the part we need.

cycle takes a sequence and returns an infinitely repeating sequence.

(take 10 (cycle [1 2 3]))
(1 2 3 1 2 3 1 2 3 1)

iterate applies a function f to x, then f to that result, and so on, infinitely.

(take 10 (iterate #(* % 2) 1))
(1 2 4 8 16 32 64 128 256 512)

If we don’t want our sequence functions to be lazy, we use versions that return vectors, which are not lazy sequences.

(mapv #(* % 2) [1 2 3])
[2 4 6]
(filterv even? [1 2 3 4])
[2 4]

Threading Macros

Threading Macros are my favorite part of Clojure. Commonly, we perform sequence functions one after another in a pattern similar to map, then filter, then reduce.

In JavaScript, we do this via method chaining:

[1, 2, 3, 4]
    .map(x => x * 3)
    .filter(x => x % 2 === 0)
    .reduce((acc, x) => acc + x);
18

This looks nice and is easy to read. In Lisps, we would traditionally nest these function calls.

(reduce + (filter even? (map #(* % 3) [1 2 3 4])))
18

This isn’t too bad, but you can see how this gets out of hand. That is why Clojure introduced the threading macros. They allow a convenient format for us to repeatedly apply transformations to our data.

There is the thread-last macro, ->>, that takes a value and applies it as the last argument to the first expression, applies the result of that expression as the last argument to the next, and so on. It is easier to demonstrate:

(->> [1 2 3 4]
     (map #(* % 2)) ;; [3 6 9 12]
     (filter even?) ;; [6 12]
     (reduce +))    ;; 18
18

If you are not sure what is happening, let me illustrate with commas indicating where the value is applied. (Remember, commas are whitespace, we can put them anywhere).

(->> [1 2 3 4]
     (map #(* % 2) ,,,) ;; [3 6 9 12]
     (filter even? ,,,) ;; [6 12]
     (reduce + ,,,))    ;; 18

We map over [1 2 3 4], we filter over that result, and then reduce it.

The advantage over method chaining is clear as soon as you hit a JavaScript function that is not a method.

Thread-last is traditionally used on operations that involve transforming sequences. You will notice those functions take the collection last.

You may have guessed there is also thread-first macro, ->. It is often used when transforming singular data types and also when modifying maps. It applies the data, and intermittent values, as the first argument to each expression.

(-> {:a 1 :b 2}
    (assoc ,,, :c 3)
    (dissoc ,,, :a))
{:b 2, :c 3}
(-> "     test string     "
    (clojure.string/trim ,,,)
    (clojure.string/upper-case ,,,)
    (clojure.string/reverse ,,,))
"GNIRTS TSET"

Loops

We haven’t talked about for or while loops yet. In most Lisps, we avoid these by instead using recursion. Lisps tend to have a feature called tail-call optimization that allows us to recur without allocating memory for each function call added to the stack. Because Clojure is built on top of Java it does not have this feature, and likely never will.

Instead, it has a loop macro that enables us to perform a sort of recursion. It takes a vector of variables and expressions, and binds the variables to the values of those expressions, just like let.

Then, in its body, it either evaluates to a value, or calls recur. recur executes the loop body again, but binds the variables to the arguments.

(loop [variable1 expression1
       variable2 expresssion2
       ...]
  (body))

(loop [variable1 expression1
       variable2 expresssion2
       ...]
  ;; commonly
  (if some-test
    result
    (recur (transform variable1) (transform variable2))))

Let’s implement summing a list using traditional recursion.

(defn sum-recursively [acc nums]
  (if (empty? nums)
    acc
    (sum-recursively (+ acc (first nums)) (rest nums))))

(defn sum [coll]
  (sum-recursively 0 coll))

(sum [1 2 3 4 5])
15

In most other Lisps, this would be fine, but if we try it on a large collection we will get a stack overflow.

(sum (range 10000000))
class java.lang.StackOverflowErrorclass java.lang.StackOverflowErrorExecution error (StackOverflowError) at user/eval5761$sum-recursively (REPL:5).

However, with some minor modifications we can safely use loop.

(defn sum [coll]
  (loop [acc 0
         nums coll]
    (if (empty? nums)
      acc
      (recur (+ acc (first nums)) (rest nums)))))

(sum [1 2 3 4 5])
15
(sum (range 10000000))
49999995000000

Example Problems

To give some context to what we have talked about, and some additional content, here are some coding challenges to attempt and/or read to get a feel for Clojure.

Longest String in an Array

Find the longest string in a given array.

JavaScript

const strings = ["short", "really really long!", "medium"]
strings.reduce((acc, x) => (x.length > acc.length ? x : acc))
"really really long!"

Clojure

(def strings ["short" "really really long!" "medium"])
(reduce #(if (> (count %2) (count %1)) %2 %1) strings)
"really really long!"

Advanced Solution: Clojure has max-key, which applies a function to its other arguments and returns the value that gives the maximum result.

(def strings ["short" "really really long!" "medium"])
(apply max-key count strings)
"really really long!"

We must use apply because max-key expects elements given as arguments and not a collection. The apply macro is similar to using the JS spread operator, ..., to the final collection in the argument list.

Reverse a string

JavaScript

function reverseString(str) {
  return str.split("").reduce(function (a, b) {return b + a});
}
reverseString("Hello, World!")
"!dlroW ,olleH"

Clojure

(clojure.string/join (reverse "Hello, World!"))
"!dlroW ,olleH"

Even Occurrences

Find the first item that occurs an even number of times in an array. Test Array: [1, 7, 2, 4, 5, 6, 8, 9, 6, 4].

JavaScript

const arr = [1, 7, 2, 4, 5, 6, 8, 9, 6, 4];
function evenOccurrence(arr) {
  const even = arr.reduce((a, x) => (Object.assign(a, {[x]: !(a[x] === undefined || a[x])})), {});
  return arr.find(x => even[x]) || null;
}
evenOccurrence(arr);
4

Clojure

(def arr [1, 7, 2, 4, 5, 6, 8, 9, 6, 4]);
(defn even-occurrence [coll]
  (let [freqs (frequencies coll)]
    (some #(when (even? (get freqs %)) %) coll)))
(even-occurrence arr)
4

Not that the Clojure solution correctly handles the case where we have both integer and string elements that have the same value, as both will be distinct map keys, unlike in the JS solution where they are coerced to strings.

Sum Digits

Write a function that takes a positive integer and returns the sum of its individual digits.

JavaScript

function sumOfDigits(num) {
    let acc = 0;
    do {
        acc += num % 10;
        num = Math.floor(num / 10);
    } while (num > 0);
    return acc;
}
sumOfDigits(490682);
29

Clojure

(defn sum-digits [int]
  (->> int
       (iterate #(quot % 10))
       (take-while pos?)
       (map #(mod % 10))
       (reduce +)))
(sum-digits 490682)
29

Destructuring and Function Arguments

At this point you have all the tools you need to start reading and writing Clojure. However, there is still a lot about the language we haven’t touched on.

Clojure and JavaScript share the convenient ability to destructure data. It is most commonly seen in function arguments, in let and in loop declarations.

Sequences are destructured intuitively by placing a variable in each position of the collection:

(let [[a b c] [1 2 3]]
  (println a b c))
1 2 3

Instead of the spread operator, an & is used to indicate we would like to collect the rest of elements of a sequence:

(let [[x & xs] [1 2 3 4]]
  (println x)
  (println xs))
1
(2 3 4)

Note that the vector was destructured into a list.

We can also destructure while preserving a variable referencing the whole structure using the :as keyword.

(let [[x & xs :as coll] [1 2 3 4]]
  (println x)
  (println xs)
  (println coll))
1
(2 3 4)
[1 2 3 4]

Map destructuring is slightly different. We specify a variable name and the key whose value we would like to associate it with.

(let [{a :a, b :b, c :c} {:a 1, :b 2}]
  (println a b c))
1 2 nil

When we try to destructure a value not present in our data, it is assigned nil. With maps, we can specify default data with :or.

(let [{a :a, b :b, c :c, :or {c 8, b 12}} {:a 1, :b 2}]
  (println a b c))
1 2 8

This is still pretty verbose, so there is an alternate syntax where we do automatically associate keys with the appropriate variable name.

(let [{:keys [abc def ghi]} {:abc 1, :def 2, :ghi 3}]
  (println abc def ghi))
1 2 3

But because we can use anything as a key, there is instead :strs and :syms for string and symbol keys respectively.

(let [{:strs [abc def ghi]} {"abc" 1, "def" 2, "ghi" 3}]
  (println abc def ghi))
1 2 3
(let [{:syms [abc def ghi]} {'abc 1, 'def 2, 'ghi 3}]
  (println abc def ghi))
1 2 3

We can use all of these in function arguments as well.

(defn boxed-inc [[x]] [(+ x 1)])
(boxed-inc [2])
[3]

There is a special case of destructuring used for when we want to give functions keyword arguments.

(defn sample-fn [a b c & {:keys [foo bar baz]
                          :or {foo true, bar false}}]
  (println a b c foo bar baz))

(sample-fn 1 2 3 :bar "bar")
1 2 3 true "bar" nil

Keyword arguments are inherently optional and will be assigned nil if a default value is not given.

Atoms and Transients

While all of our standard data structures and variables are immutable, there are two common cases where we want and can use mutation.

The first is the atom. Atoms hold one of our existing data structures. We can dereference it with the deref function or the @ operator to get the value it is holding. swap! updates the value by applying a function, and reset! sets it to the given value.

(let [counter (atom 1)]
  (println (deref counter))
  (swap! counter inc)
  (println @counter)
  (reset! counter 10)
  @counter)
1
2
10

All of the functions that perform mutations are explicitly marked with an ! at the end. All operations on atoms are synchronous. We will see them used when we get to the frontend and start building components.

We also have transients. Under the hood, Clojure will create mutable data structures to perform efficient operations before returning the immutable data structure for our use. Transients let us turn our immutable data structures into mutable versions. That is not to say they work like mutable arrays in JavaScript.

We call transient on a data structure to create a mutable copy. It can only be modified using special mutable versions of our traditional operators, like conj!, accoc! and so on. Using a normal conj or other persistent function will throw an error, making it impossible to accidentally mix in transient structures. We call persistent! to make our structure immutable.

Transients are distinct from atoms in that they are not meant to be modified and used independently.

They are correctly used exactly the same as persistent structures. They main exceptions being old references to them will be mutated and they are not safe to use in multiple threads (something we get for free with persistent data).

(def coll (transient [1 2 3 4]))
(-> coll
    (conj! 5)
    (assoc! 0 0)
    persistent!)
[0 2 3 4 5]

Let’s see what happens when we try to continue modifying coll

(assoc! coll 0 1)
class java.lang.IllegalAccessErrorclass java.lang.IllegalAccessErrorExecution error (IllegalAccessError) at user/eval7628 (REPL:2).
Transient used after persistent! call

That’s a helpful error message. persistent! has an exclamation point, so it is telling us that it mutates the data is given. When we apply it we don’t just return a persistent data structure, we mutate our transient into one and cannot continue using old references.

Developing using lein

Installing Clojure and lein

Link to instructions

Initializing a project

lein new name Project structure

project.clj and deps

Adding dep vectors installing

namespaces and requiring

Other resources