December 5, 2020

Learning Clojure

So, for various reasons, I have decided to take a look at Clojure. It is a programming language that implements LISP yet runs on top of the JVM (Java Virtual Machine). For those with long and geeky memories, LISP is one of the programming languages that ESR recommends every programmer learn.

It is a requirement that when learning a new programming language the programmer is obligated to write as their first program the iconic hello world program. While normally a breaker of rules, this one makes sense. Further, I recall writing about Learning Projects and this seemed to be a good time to put my own words into action. Naturally, I will start with hello world.

Caveat: I'm still very much at the early stages of figuring this out, so everything after this caveat may be less than 100% correct. I'll come back later and correct mistakes as I find them.

If you use Leiningen as your Clojure build and project management tool (as seems to be strongly recommended on the Clojure homepage), you get a free hello world program everytime you start a new application. This is helpful in that it is guaranteed to work, but unhelpful that you don't have to find a version of hello world and type it in yourself. (Copy and paste vs. typing from printed when learning a new programming language is a whole topic for another day!) So, like it or not after typing:

lein new app hello

you get a fully functional hello world app waiting for you to just type:

lein run

Given that lein is going to give us a standard copy of hello world whether we want it to or not, it seems that perhaps our task here is to ensure that we understand it rather than bemoan the fact that we didn't get to type it ourselves. Let's take a look at it them.

(ns hello.core
  (:gen-class))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (println "Hello, World!"))

I am absolutely still a Clojure newbie, so this description will be my current best understanding of what all of that means. (I have Java experience, so that helps.)

Clojure seems to love namespaces and the first expression in this program starts with ns to indicate that it is declaring that this code operates within the named namespace, in this case hello.core. The convention for namespaces seems to be that they start with the name of the app, in this case hello and then uses periods (or fullstops) to separate the sub-parts of the namespace. The majority of example code that I have seen on the Interwebs seems to use core.clj as the name of the main Clojure file within a namespace. I don't know yet if that is mandatory or just an agreed convention. Either way, it explains why the namespace for this hello world program ends with core.

Within the namespace declaration is a keyword. It begins with a colon and acts not unlike an atom in Erlang. This particular keyword in this situation seems to be necessary to tell the Clojure system that the -main function should be made available to call as if it were a Java public static void main(String[]) method.

Now we get to the function definition. Apparently, -main is the default method to call within the Clojure file. I'm sure that this can be changed with the use of options or lots of keywords, but for a simple hello world going with the defaults makes plenty of sense. There are a number of different ways to define functions in Clojure, but here the defn is the one for named functions and all that I'm going to worry about this time around.

After the function name comes the optional function description. It is absolutely optional, but lein gives us one whether we wanted it or not. It seems unhelpful that the provided function description is not useful. This seemed like a great opportunity for a good example of the kind of short document description that the Clojure community would recommend providing. Still, going forward it is good to know that we can easily provide a one line description of our functions.

While we don't generally pass any command line arguments to a hello world program, the initial called method is generally passed some kind of reference/pointer/list of the arguments provided. Where no arguments are provided, that reference/pointer/list will be null or empty, so for this program we are going to completely ignore the args thing and take it up in one of the next programs.

Finally, we arrive at the core of the hello world program, the part that sends the words "hello, world" to the screen (or standard output if you want to sound old school). Almost every programming language has some kind of facility, be it a built-in keyword or a function in the language's standard library, to send text to the screen. Clojure is no different. It has the function println, which should look suspiciously familiar to anyone with Java on their resume. The println function takes a string argument, displays it on the screen and then follows up with a newline character to finish off the line and move the output point to the next line.

Provided that you haven't messed with the supplied code, this should run and display those venerable words "hello, world", albeit with more capitalization than I remember from my first reading of K&R!

Tags: Clojure