Guide to starting with Clojure

This guide is aimed to the developer who’s interested in Clojure and want to install it and start using it right away.

It will cover the installation of Clojure, some of the build tools and how they work, the configuration of an editor, of the REPL and a small hello world demonstrating how things like build, and test works.

If you’re new to Clojure, at the end of this guide you should be able to start your project.

Table of contents

Installing

Java

First you’ll need to make sure that Java is installed on your computer, you can run a Terminal and run the following command to check if it’s already installed:

You’ll need at least Java 8, but you can install Java 11, Java 12 or even Java 13.

If you don’t have Java, or want to upgrade it you can either go download it directly from the Oracle website, or you can use Adopt OpenJDK and select the version that you wish to install.

Once you installed or upgraded java run again java -version in your terminal to check that it’s been correctly installed, for example this is a sample output after having downloaded and installed Java 11 (here in Ubuntu via Windows SubLinux) :

$ java -version
openjdk version "11.0.5" 2019-10-15
OpenJDK Runtime Environment (build 11.0.5+10-post-Ubuntu-0ubuntu1.118.04)
OpenJDK 64-Bit Server VM (build 11.0.5+10-post-Ubuntu-0ubuntu1.118.04, mixed mode, sharing)

Clojure Tooling

There are different Clojure tools that you can use to build your project.

  1. Clojure CLI + tools.deps
  2. Leiningen
  3. Boot

You can use the one you want.

Hey I’m new to Clojure and you want me to make a choice? Seriously?

I agree with you, humble imaginary visitor.

In this guide I will cover tools.deps, and we’ll also install Leiningen so that you have it on your system when you need to, and because I’ve never used Boot myself even if I’ve heard good things about it, you can try it if you want.

From my point of view you should start with tools.deps because I feel like the community is really invested in it right now, and they are building some cool stuff.

Nevertheless it helps knowing how Leiningen works because you’ll find a lot of projects using it. But for your project just stick with one of the alternative, don’t try to mix the two.

Edit: I’ve just found this article What are the clojure tools which goes into more details regarding how the Clojure CLI & tools.deps actually work, so read it later if you need to know more about it.

MacOS

You can install Clojure using homebrew:

First update your brew, then install Clojure CLI, and verify that it works:

$ brew update
$ brew install clojure/tools/clojure
$ clj
Clojure 1.10.1
user=> (println "Hello, world!")
Hello, world!
nil
user=> 

With these 3 commands you have Clojure CLI + tools.deps already installed.

Now let’s install Leiningen, you just need to download the lein script make it executable and put it somewhere on your disk.

I personally install all my dev tools in ~/dev/tools/, but you can install it wherever you want like in /usr/local/bin/, for example

$ curl https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein > lein
$ sudo mv lein /usr/local/bin/lein
$ sudo chmod +x /usr/local/bin/lein
$ lein version
Leiningen 2.9.1 on Java 11.0.5 OpenJDK 64-Bit Server VM

Linux + Windows

On Windows I personally use the Windows Sub Linux (WSL), I chose the Ubuntu distribution but you can take anyone you prefer, then open your Ubuntu terminal and follow the steps.

You can install Clojure by downloading an installer from the clojure.org website

$ curl -O https://download.clojure.org/install/linux-install-1.10.1.536.sh
$ chmod +x linux-install-1.10.1.536.sh
$ sudo ./linux-install-1.10.1.536.sh
$ clj
Clojure 1.10.1
user=> (println "Hello, world!")
Hello, world!
nil
user=> 

Now you have Clojure CLI + tools.deps installed correctly.

To install Leiningen, you’ll do the same as on Mac OS X

$ curl https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein > lein
$ sudo mv lein /usr/local/bin/lein
$ sudo chmod +x /usr/local/bin/lein
$ lein version
Leiningen 2.9.1 on Java 11.0.5 OpenJDK 64-Bit Server VM

IDE

Now that Clojure is installed, let’s write some code using your IDE and the REPL integration.

Obviously I cannot explain how every IDE / text editor works with Clojure, I’m not a Emacs or Atom user, I do use mainly IntelliJ, but I’ve used VSCode extensively also.

As with the Linux distribution, you can chose to use the editor you like most, like Emacs, or Vim + fireplace, or Sublime or any other you can find.

This is why I will cover both IntelliJ and VSCode in this guide.

IntelliJ + Cursive

Since I’m from the Java world, I’m used to IntelliJ and the Cursive plugin for Clojure development.

If you don’t have IntelliJ installed, just grab a version, the community version is free and works fine with Cursive, otherwise just follow the steps for VS Code below.

Run the installer then run IntelliJ.

Type Shift Shift then type Plugins then Enter or you can go to File > Settings... > Plugins.

In the Plugins window type Cursive and then install it, finally restart the IDE, you’ll be good to go for the next section.

VSCode + Calva

Regarding VSCode, you can use the Calva plugin, to install it you just run VSCode and the go to the Extension market place, search for Calva and then install it, you’re good to go.

Playing with the REPL from your IDE

The Clojure REPL is fantastic, and I urge you to make it part of your work-flow right from the beginning.

If you want a demo of how the REPL can help you write your code when you know how to use it (and it’s quick to get going), just take a look at What makes a good REPL? from Valentin Waeselynck.

Note: for this part I will cover opening a project based on tools.deps, but the IDE support opening Leiningen projects also, and it doesn’t affect the REPL in any way.

There are two ways to work with the REPL, either you run a REPL from the command line, and then you attach your IDE to it, or you run a REPL directly from your IDE.

I usually chose the first solution using nREPL and then attach my Cursive within IntelliJ to it. We’ll see both solutions for both of the IDE and how we can do some trivial things with the REPL to get you going.

In order to have the same little project to test from both IDE, let’s just create a simple file named deps.edn and put the following content in it.

{:paths ["resources" "src"]
 :deps {org.clojure/clojure {:mvn/version "1.10.1"}
        nrepl/nrepl {:mvn/version "0.5.3"}}}

Then create a simple file named src/playground.clj with the following content:

Ok, we’re good to go.

IntelliJ + Cursive

Importing this project in IntelliJ

Just go through File > Open and select the directory where you’re working, then open as a New Project.

IntelliJ will open a new window with your project.

Now let’s see how to use the REPL from within IntelliJ.

Run a REPL from your IDE

Click the Add Configuration… button next to the green hammer.

Then click the + button and chose Clojure REPL > Local, in the new Run/Debug Configurations window name it Local REPL and click OK.

Now instead of the Add Configurations button, select Local REPL and click run.

You can evaluate Clojure from the text editor in the bottom right (where I wrote (println "Hello, world!") then press Enter.

You can also directly evaluate forms from your editing, for example place your cursor at the end of (+ 40 2) and press Alt + Shift + P.

For example type a new Clojure code (map inc [0 1 2 3]) put your cursor at the end, then press Alt + Shift + P it will send it for evaluation into the REPL, which will respond with => (1 2 3 4).

Now in the text editor in the bottom right, type (map inc *1) then press Enter, it will output => (2 3 4 5). Because in the REPL, *1 means the last evaluated result.

Cursive let’s you load a file to the REPL (Alt + Shift + L), synchronize the files to the REPL (Alt + Shift + M), switch the REPL to the current namespace you’re editing (Alt + Shift + R).

One interesting binding to set with a custom shortcut is the Send form before carret to REPL that you can assign in the Keymap preferences of Intellij like this:

Then with your cursor at the end of a Clojure form just press Ctrl + Enter and it’s sent to the REPL for evaluation. You can set any shortcut you want, like Alt + Shift + E (for evaluate).

I’ll let you play with the different options that you can find in the Tools > REPL menu.

Attach to a running REPL

In order to attach to a REPL you must have one running elsewhere, if you followed the section above, stop the REPL by clicking the red square.

Now in a terminal go to the directory where your code is located then type the following.

$ clj -m nrepl.cmdline
nREPL server started on port 63812 on host localhost - nrepl://localhost:63812

Take a look at the port on which nREPL is running, for me it was 63812.

In IntelliJ, click the Add Configuration… button next to the green hammer.

Then click the + button and chose Clojure REPL > Remote, in the new Run/Debug Configurations window name it Remote REPL, select nREPL then type localhost for the host and 63456 for the ** port**, finally click OK.

Now instead of the Add Configurations button, select Remote REPL and click run.

As for the previous section you can evaluate into the remote REPL as if it was running locally. A remote REPL has advantages, you could imagine that you connect to a running REPL in production and check what’s going on live.

VSCode + Calva

Importing this project in VSCode

In VSCode, just go to Open > Folder, select the folder where your code is located and click OK.

Run a REPL from your IDE

Right click in the editor and select Jack-in or Connect to REPL Server

Select Start a REPL server and connect (a.k.a Jack-in), then select Clojure CLI.

Like Cursive in IntelliJ, it will display a REPL on the right side of your IDE, where you can evaluate Clojure forms.

You can select a form using Ctrl + Alt + C, Ctrl + S, and evaluate it using Ctrl + Alt + C V, the result is directly appended next to your form in your IDE.

Calva has plenty of shortcuts like loading the current file in the REPL (Ctrl + Alt + C Enter), load the current namespace in the REPL (Ctrl + Alt + C, Ctrl + Alt + N).

I’ve lost my finger muscle memory regarding the default shortcuts on Calva, but I think they can be customized to your need (I’m more of a Cursive guy).

If you want to disconnect from the REPL, just right click then select Disconnect from the REPL server.

Attach to a running REPL

Right click in the editor and select Jack-in or Connect to REPL Server

This time select Connect to a running REPL server in your project then Clojure CLI then type localhost:63812 if your previous REPL is still running, otherwise run it again and get the actual nREPL port.

Accept by pressing Enter and you’re good to go (see the green text saying REPL connected.).

All right you’re now ready to rock!

Clojure 101 in 5 minutes

Tables Cool
Numbers Integers, doubles, floats, fractions
String "foo"
Character A
Keyword :foo
List '(1 2 3)
Vector [1 2 3]
Map {:name "Alex" :age 35}
Sets #{1 2 3}
Regexp #"d+"
Variable (def foo 123)
Function clojure (defn add [a b] (+ a b)
Anonymous function (fn [a b] (+ a b))
Also more concise #(+ %1 %2)
Java JavaScript Clojure
String "foo" "foo" or 'foo' "foo"
Boolean true, false true, false true, false
Nothing null null nil
Keywords Nope Nope :name
Numbers 1 1 1
Decimals 1.2 1.2 1.2
Comments //comment //comment ; comment
Regexp "\d+" /d/ #"d+"
Arrays/Vectors new int[] {1, 2, 3} [1, 2, 3] [1 2 3]
Maps Map.of("one", 1, "two", 2) {one: 1, two: 2} {:one 1 :two 2}
Sets Set.of(1, 2, 3) new Set(1, 2, 3) #{1 2 3}
Functions int sum(int x, int y) { return x + y; } function sum(x, y) { return x + y; } (defn sum [x y] (+ x y))
Shorthand sum = (x, y) -> x + y; const sum = (x, y) => x + y; (def sum #(+ %1 %2))
Function call sum(1, 2) sum(1, 2) (sum 1 2)

Project specific steps

I want to build my project

Note: for this example I just created an src folder, but you’ll often see src/main/clojure deriving from Maven standard directory layout, in such a case you just need to change the path in deps.edn.

Let’s say you have written a super program which SHOUT the text you’re giving to it.

(ns playground
   (:require [clojure.string :as str])
   (:gen-class))

(defn shout [text]
   (as-> (seq text) $
     (interpose   $)
     (concat $ (repeat 3 !))
     (apply str $)
     (str/upper-case $)))

(defn -main [& args]
    (->> args
         (map shout)
         (apply str)
         println))

You can run it with the clj utility:

$ clj -m playground "Clojure is fun"
C L O J U R E   I S   F U N!!!

But we’d like to build this to a fat JAR (some call them uber JAR also). A fat JAR is a Java Archive (a zip) containing all the needed dependencies so that you just have to call the java executable on it to execute the code it contains.

We call them Jean-Michel at work since years, because of Jean-Michel Jarre. Hey, t’as buildé le Jean-Michel ?

There are multiple tools which can do it in Clojure land, but we’ll be using uberdeps by Nikita Tonsky, one of the Clojure community that you should definitely follow, but you’ll see more at the end of this article.

Modify your deps.edn file to add uberdeps :

{:paths ["resources" "src"]
 :deps {org.clojure/clojure {:mvn/version "1.10.1"}
        nrepl/nrepl {:mvn/version "0.5.3"}}
 :aliases {:fatjar {:extra-deps {uberdeps {:mvn/version "0.1.10"}}
                    :main-opts  ["-m" "uberdeps.uberjar" "--target" "target/shout-0.1.jar"]}}}

Essentially we’re just adding an alias named fatjar that needs an extra dependency uberdeps and which command line to run is clj -m uberdeps.uberjar --target target/shout-0.1.jar.

In order to run an alias you have to use clj -A{alias-name}, so just do it like this, and then run the JAR:

$ clj -Afatjar
Downloading: uberdeps/uberdeps/0.1.10/uberdeps-0.1.10.pom from https://repo.clojars.org/
Downloading: org/clojure/tools.deps.alpha/0.8.677/tools.deps.alpha-0.8.677.pom from https://repo1.maven.org/maven2/
...
[uberdeps] Packaging target/shout-0.1.jar...
+ resources/**
+ src/**
+ nrepl/nrepl .5.3
.   nrepl/bencode 1.0.0
+ org.clojure/clojure 1.10.1
.   org.clojure/core.specs.alpha .2.44
.   org.clojure/spec.alpha .2.176
[uberdeps] Packaged target/shout-0.1.jar in 572 ms

$ java -cp target/shout-0.1.jar clojure.main -m playground "Clojure is fun"
C L O J U R E   I S   F U N!!!

Working as expected, now you can just send your JAR to anyone having Java 8+ installed, and they can use it. How cool!

And tests? Everyone say tests are great and all

Like for the fat JAR, just add an alias to your deps.edn so that we can use kaocha from lambdaisland (one more to follow) as a test runner.

{:paths ["resources" "src"]
 :deps {org.clojure/clojure {:mvn/version "1.10.1"}
        nrepl/nrepl {:mvn/version "0.5.3"}}
 :aliases {:test   {:extra-paths ["test"]
                    :extra-deps  {lambdaisland/kaocha {:mvn/version "1.0-612"}}
                    :main-opts   ["-m" "kaocha.runner"]}
           :fatjar {:extra-deps {uberdeps {:mvn/version "0.1.10"}}
                    :main-opts  ["-m" "uberdeps.uberjar" "--target" "target/shout-0.1.jar"]}}}

Now just create a test directory, and add a file named playground_test.clj write a simple test:

(ns playground-test
  (:require [clojure.test :refer :all]
            [playground :refer :all]))

(deftest shouting
  (is (= "H E L L O!!!" (shout "hello"))))

Note that the file is named playground_test with an underscore but the namespace is named playground-test with a dash.

Then run kaocha with clj and your newly created test alias.

$ clj -Atest
[(.)]
1 tests, 1 assertions,  failures.

Great we have created a powerful test, and we can now ensure our program works before building it and send it to friends!

I’m a beginner, I make mistake, is there something to help me?

You can use a linter. In Clojure there are plenty of options, but the most recent is clj-kondo by Michiel Borkent (one more to follow).

You can install the binary, or use it directly from your deps.edn, this is what I will cover below, but don’t hesitate to read the README of clj-kondo.

{:paths ["resources" "src"]
 :deps {org.clojure/clojure {:mvn/version "1.10.1"}
        nrepl/nrepl {:mvn/version "0.5.3"}}
 :aliases {:test   {:extra-paths ["test"]
                    :extra-deps  {lambdaisland/kaocha {:mvn/version "1.0-612"}}
                    :main-opts   ["-m" "kaocha.runner"]}
           :fatjar {:extra-deps {uberdeps {:mvn/version "0.1.10"}}
                    :main-opts  ["-m" "uberdeps.uberjar" "--target" "target/shout-0.1.jar"]}
           :kondo  {:extra-deps {clj-kondo {:mvn/version "RELEASE"}}
                    :main-opts  ["-m" "clj-kondo.main"]}}}

For the purpose of the article, modify the playground.clj to add an unused binding:

(ns playground
   (:require [clojure.string :as str])
   (:gen-class))

; ...

(defn -main [& args]
  (let [foo "foo bar"]
    (->> args
         (map shout)
         (apply str)
         println)))

Then run clj-kondo:

$ clj -Akondo
src/playground.clj:13:9: warning: unused binding foo
linting took 60ms, errors: , warnings: 1

You’ll notice that clj-kondo took 60ms to execute, but it took more than that to execute clojure and everything related, that’s why clj-kondo can also be installed as a native binary.

Let’s install it, and run it to see that the execution is instant.

$ bash <(curl -s https://raw.githubusercontent.com/borkdude/clj-kondo/master/script/install-clj-kondo)
$ clj-kondo --lint src
src/playground.clj:13:9: warning: unused binding foo
linting took 9ms, errors: , warnings: 1

That seems about very usable directly from your IDE, you can achieve this by following the great guide on the clj-kondo GitHub wiki.

Is there any way my code looks the same everywhere?

Sure use a code formatter, they are built-in your IDE, in IntelliJ I belive it’s Ctrl + Alt + L (or Menu > Code > Reformat code), but maybe you want to reformat the code in a git pre-hook?

For this you can use cljfmt by weavejester (again someone to follow).

Modify your deps.edn and add both a fmt-fix and fmt-check aliases:

{:paths ["resources" "src"]
 :deps {org.clojure/clojure {:mvn/version "1.10.1"}
        nrepl/nrepl {:mvn/version "0.5.3"}}
 :aliases {:test      {:extra-paths ["test"]
                       :extra-deps  {lambdaisland/kaocha {:mvn/version "1.0-612"}}
                       :main-opts   ["-m" "kaocha.runner"]}
           :fatjar    {:extra-deps {uberdeps {:mvn/version "0.1.10"}}
                       :main-opts  ["-m" "uberdeps.uberjar" "--target" "target/shout-0.1.jar"]}
           :kondo     {:extra-deps {clj-kondo {:mvn/version "RELEASE"}}
                       :main-opts  ["-m" "clj-kondo.main" "--lint" "src"]}
           :fmt-fix   {:extra-deps {com.jameslaverack/cljfmt-runner
                                    {:git/url "https://github.com/JamesLaverack/cljfmt-runner"
                                     :sha "97960e9a6464935534b5a6bab529e063d0027128"}}
                       :main-opts ["-m" "cljfmt-runner.fix"]}
           :fmt-check {:extra-deps {com.jameslaverack/cljfmt-runner
                                    {:git/url "https://github.com/JamesLaverack/cljfmt-runner"
                                     :sha "97960e9a6464935534b5a6bab529e063d0027128"}}
                       :main-opts ["-m" "cljfmt-runner.check"]}}}

Now modify the playground.clj to make it look weird on purpose.

(ns playground
  (:require [clojure.string :as str])
  (:gen-class))

(defn shout [text]
  (as-> (seq text) $
              (interpose   $)
    (concat $ (repeat 3 !))
                       (apply str $)
           (str/upper-case $)))

(defn -main [& args]
                 (let [foo "foo bar"]
    (->> args
                      (map shout)
         (apply str)
                        println)))

And run cljfmt:

$ clj -Afmt-check
src/playground.clj has incorrect formatting
--- a/mnt/c/temp/starter/src/playground.clj
+++ b/mnt/c/temp/starter/src/playground.clj
@@ -4,14 +4,14 @@

 (defn shout [text]
   (as-> (seq text) $
-              (interpose   $)
+    (interpose   $)
     (concat $ (repeat 3 !))
-                       (apply str $)
-           (str/upper-case $)))
+    (apply str $)
+    (str/upper-case $)))

 (defn -main [& args]
-                 (let [foo "foo bar"]
+  (let [foo "foo bar"]
     (->> args
-                      (map shout)
+         (map shout)
          (apply str)
-                        println)))
+         println)))
No such file: project.clj

Pretty neat, now ask cljfmt to fix it:

$ clj -Afmt-fix src/playground.clj
Reformatting src/playground.clj

Done, your code looks like normal again:

(ns playground
  (:require [clojure.string :as str])
  (:gen-class))

(defn shout [text]
  (as-> (seq text) $
    (interpose   $)
    (concat $ (repeat 3 !))
    (apply str $)
    (str/upper-case $)))

(defn -main [& args]
  (let [foo "foo bar"]
    (->> args
         (map shout)
         (apply str)
         println)))

This is using cljfmt-runner for deeper integration with tools.deps.

Clojure core library is so full of awesome bits, sometimes I forget them

Of course Clojure comes with batteries included, really included, even in years you’ll discover some gems, but for a starter you can lean on kibit which will inspect your code and propose you some ways you can improve it with built-in function you forgot existed.

Modify your playground.clj to look like this:

(ns playground
  (:require [clojure.string :as str])
  (:gen-class))

(defn handle-empty [s]
  (if (nil? s)
    (seq "Please talk to me")
    s))

(defn shout [text]
  (as-> (seq text) $
    (handle-empty $)
    (interpose   $)
    (concat $ (repeat 3 !))
    (apply str $)
    (str/upper-case $)))

(defn -main [& args]
    (->> (or args [""])
         (map shout)
         (apply str)
         println))

Test it still works:

$ clj -m playground "Hello"
H E L L O!!!
$ clj -m playground ""
P L E A S E   T A L K   T O   M E!!!
$ clj -m playground
P L E A S E   T A L K   T O   M E!!!

You can also add a test to your playground_test.clj

(ns playground-test
  (:require [clojure.test :refer :all]
            [playground :refer :all]))

(deftest shouting
  (is (= "H E L L O!!!" (shout "hello"))))

(deftest shouting-empty
  (let [expected "P L E A S E   T A L K   T O   M E!!!"]
    (is (= expected (shout nil)))
    (is (= expected (shout "")))))

Then run the tests:

$ clj -Atest
[(...)]
2 tests, 3 assertions,  failures.

Ok let’s get back to kibit.

Add it to your deps.edn like this:

{:paths ["resources" "src"]
 :deps {org.clojure/clojure {:mvn/version "1.10.1"}
        nrepl/nrepl {:mvn/version "0.5.3"}}
 :aliases {:test      {:extra-paths ["test"]
                       :extra-deps  {lambdaisland/kaocha {:mvn/version "1.0-612"}}
                       :main-opts   ["-m" "kaocha.runner"]}
           :fatjar    {:extra-deps {uberdeps {:mvn/version "0.1.10"}}
                       :main-opts  ["-m" "uberdeps.uberjar" "--target" "target/shout-0.1.jar"]}
           :kondo     {:extra-deps {clj-kondo {:mvn/version "RELEASE"}}
                       :main-opts  ["-m" "clj-kondo.main" "--lint" "src"]}
           :fmt-fix   {:extra-deps {com.jameslaverack/cljfmt-runner
                                    {:git/url "https://github.com/JamesLaverack/cljfmt-runner"
                                     :sha "97960e9a6464935534b5a6bab529e063d0027128"}}
                       :main-opts ["-m" "cljfmt-runner.fix"]}
           :fmt-check {:extra-deps {com.jameslaverack/cljfmt-runner
                                    {:git/url "https://github.com/JamesLaverack/cljfmt-runner"
                                     :sha "97960e9a6464935534b5a6bab529e063d0027128"}}
                       :main-opts ["-m" "cljfmt-runner.check"]}
           :kibit     {:extra-deps {tvaughan/kibit-runner {:mvn/version "0.1.0"}}
                       :main-opts  ["-m" "kibit-runner.cmdline"]}}}

And run kibit

$ clj -Akibit
At ./src/playground.clj:15:
Consider using:
  (clojure.string/join $)
instead of:
  (apply str $)

Kibit can also report the changes to be made in Markdown

$ clj -Akibit -- --reporter markdown
----
##### `./src/playground.clj:15`
Consider using:
​```clojure
  (clojure.string/join $)```
instead of:
​```clojure
  (apply str $)```

And you can ask it to replace with what it thinks best alone (add --interactive for an interactive terminal session)

$ clj -Akibit -- --replace
Replacing
  (apply str $)
 with
  (clojure.string/join $)
in ./src/playground.clj:15

Now your code looks like this

(ns playground
  (:require [clojure.string :as str])
  (:gen-class))

(defn handle-empty [s]
  (if (nil? s)
    (seq "Please talk to me")
    s))

(defn shout [text]
  (as-> (seq text) $
    (handle-empty $)
    (interpose   $)
    (concat $ (repeat 3 !))
    (clojure.string/join $)
    (str/upper-case $)))

(defn -main [& args]
    (->> (or args [])
         (map shout)
         (apply str)
         println))

Awesome, isn’t it?

Where to go from here?

Now that your IDE is configured and you have some tooling ready to rock some code, how do you start your Clojure journey?

I’ll link to you some website and tutorials to read, and also people to follow.

Websites

Conferences

  • Clojure/north
  • :clojureD
  • IN/Clojure
  • ClojuTRE
  • Clojure/conj

Follow

Last word

Clojure is awesome, there’s even love letters written to it,
the community is fantastic, the projects are of great quality.

I’ve been doing Clojure for around 6 years, that’s my favorite language, it made me learn a lot, and changed how I program
in other languages (my primary language at work being Java).

I hope you’ll enjoy your Clojure journey, this is just the beginning ❤

Until next time!

Read More