Getting Started with Clojure
Time to read: 7 minutes
JavaScript has been my go-to language for quite some time now and before that it was Ruby. Both of these languages have a lovely dash of functional programming that I’ve toyed with here and there. It seems functional concepts are becoming more and more prominent amongst the JS community with projects like Redux and Immutable.js gaining popularity. I recently decided to take the dive into functional programming. A friend recommended the Clojure programming language and learning it has been a real hoot.
Clojure was created by Rich Hickey who had a very strong rationale for its birth. The language is designed to compile down to bytecode and run on a virtual machine so it’s not OS dependent. The JVM is the the most popular target but it can also compile down to JavaScript or the Microsoft CLR. Thanks to strong interoperation support you can easily take advantage of your target platform’s host language and features. We’ll see an example of this later on.
In this post we’re going to start with the basics of Clojure and its de facto build tool Leiningen. We’ll wrap up by sending our first SMS with Clojure.
I’m absolutely still an apprentice so feel free to help out below in the comments if something can be improved. I’ve found that the Clojure community is super friendly and willing to help. Heck the language is community developed.
Time To Get Technical
Let’s start by installing Leiningen. Not only does it have the best mascot logo but might just be the best build tool I’ve ever used.
The Clojure compiler is actually just a JAR file and Leiningen will take care of downloading it and doing all the heavy javac
flag and classpath lifting.
Follow the instructions based on your platform. There is also an installer for Windows and a Homebrew formula if you’re on a Mac.
We can confirm it’s installed by running the following.
This will start up a Clojure Read Eval Print Loop (REPL) and give you a nice little prompt.
Clojure is a lisp which means it uses tons of parenthesis and prefix notation. Calls to functions start with the name of the function and then the arguments. So to use the +
function to add 2 + 2
we can type the following into our REPL.
This construct is called a form
and the “words” inside of it are known as symbols. We can easily chain forms by adding more parenthesis.
Notice we don’t need to use commas to separate symbols – they are optional and treated as whitespace. The syntax will take some getting used to but I’ve found it to be very expressive.
The native data structures are powerful and are used everywhere. The most common we’ll be using are the vector and the map.
The vector is what we might think of as an array. It is a list of elements enclosed in square brackets. [1 2 3 "a" "b" "c"]
. We can generally ask for the first
item out or the rest
to perform a recursive operation on the vector.
We can also access an element at an index using the get
function.
Let’s iterate over a vector and call a function on each element using doseq
. This function expects a symbol and a collection that it will iterate over and bind to the symbol. It then calls your provided form for each item. We’re also using the println
function which, as you guessed, prints a line to the console along with the str
function which concatenates strings.
The map is our dictionary structure in Clojure. It’s created with curly braces and a set of keys and values separated by whitespace. This one’s a little tricky to get used to since we’re not visually associating the key and the value.
It’s common to use Clojure keywords as the key for a map. A keyword is a symbolic identifier that evaluates to itself. We denote a keyword with a colon.
Since this is so common we can actually use the keyword as a function and pass it a map to pull out the value.
The last building block we’ll learn is how to define our own function. To do this we use the defn
macro. The form of this is (defn name-of-function [args] (body))
. The following function say-hello
will take a name and print out a personalized greeting.
Our function can take an undefined number of arguments using the &
operator. This causes all the arguments passed in to be squished into one variable as a collection.
Finally let’s have a go at the Java interoperation that’s available. We’ll use Java’s Date class to get the current time. To do this in Java you’d run the following.
In Clojure we pass the full path and package name to the new
function.
This is done so much that there is a nice shortcut around it. Note the trailing .
.
We can call methods on the returned object with the .methodToCall function.
Even better, if we want to chain a bunch of Java method calls the ..
function makes this easy.
Hello, World!
We now have enough of a basis to create our first Clojure app. We’ll leave the REPL and use Leiningen to generate our skeleton. The quit
command should take you out of the REPL.
Create a new directory where you want to work and cd
to it in your shell. Now let’s unleash Leiningen on our new directory and have it create a blank app project for us.
There are two files in here that we’ll be working with. The first is project.clj
which is our build file for Leiningen and the second is our source file at src/hello_clojure/core.clj
. We’ll come back to the build file in a bit but for now let’s open up the source file in your favorite editor.
The top two lines set up our namespace and tell the compiler to generate a class for our app since Java uses classes and we’re running on the JVM. Following that is our main function definition which is the entry point to our app. The string after the declaration is used to document what this function does. The rest should look familiar from above.
We can tell Leiningen to compile and run our application by running
Even better we can painlessly build an uberjar that we can distribute and run on a standard JVM.
Wow that was absurdly easy.
Sending an SMS
Our program is rad but doesn’t do much. Let’s fix that by making it send a text message using the Twilio API. You’ll need your Twilio account credentials and a Twilio phone number. If you don’t have an account yet you can sign up for a free one here.
An awesome community member has created a Twilio Clojure library that makes sending an SMS a breeze. To add the dependency to our app open up project.clj
And add [twilio-api "1.0.1"]
to the :dependencies
vector.
We can have Leiningen download the dependency by running the following.
Open up core.clj
again and replace it with the following. You’ll notice a that we’re using an @
in front of the call to twilio/send-sms
. It’s a bit out of the scope of this post but that’s actually a shortcut for the deref
function which causes our program to block and wait for an asynchronous operation to complete – such as the HTTP request to Twilio’s API. If we don’t block on the HTTP request, the program will stop running before it’s made due to it being asynchronous.
Run the code one last time and take out your phone.
Next Steps
We’ve explored some of the basics of Clojure and seen how we can take advantage of the Java interop. We also wrote a function that sends an SMS using an stellar community created library. I think we can all agree that Leiningen lives up to its tagline of “… automating Clojure projects without setting your hair on fire”.
Hopefully this post has inspired you to give Clojure a try. It is a much different paradigm but pretty fun to work with.
For more reading and learning check out the book Clojure for the Brave and True by Daniel Higginbotham. And the Clojure from the ground up blog post series by Kyle Kingsbury.
Be sure to let me know what your thoughts on the language are!
Twitter: @eddiezane
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.