Clojure macros are evaluated at compile time, this enables us to do some interesting tricks. This demo shows how to generate a unique ID for each build.
First let's creat a Clojure project, assuming you've installed clojure:
clojure -M:project/new app macrodemo/playground
this will create a project in the folder playground
.
Edit src/macrodemo/playground.clj
, we define a myid
which simply is a random UUID as string:
(def myid (str (UUID/randomUUID)))
and define myid-by-macro
which is a similiar version but with macro,
(defmacro macro-random [] (str (UUID/randomUUID)))
(def myid-by-macro (macro-random))
Let's edit -main
to print out the two values twice:
(defn -main
[& _]
(dotimes [_ 2]
(println "==========================================================")
(println "myid:" myid)
(println "myid-by-macro:" myid-by-macro)))
Save and use the following command to create a uberjar
file which is basically a standalone executable jar
file you can run with Java:
clj -X:uberjar
Run the program with java -jar playground.jar
you'll see the UUIDs printed in console similiar as follows
Note the exact UUID values printed on you machine will be different.
==========================================================
myid: 47b48885-56ef-4547-b015-d85721491820
myid-by-macro: 87e2f764-d28e-4981-94b8-ad3f05a6605a
==========================================================
myid: 47b48885-56ef-4547-b015-d85721491820
myid-by-macro: 87e2f764-d28e-4981-94b8-ad3f05a6605a
First you'll notice both versions keep the values unchanged during one execution as we'd expected.
Let's run the program again with java -jar playground.jar
, you'll notice that the value of myid
has changed but the macro version myid-by-macro
remains the same:
==========================================================
myid: ad6fd1a0-71c4-4e47-8e13-fb82fcfcb224
myid-by-macro: 87e2f764-d28e-4981-94b8-ad3f05a6605a
==========================================================
myid: ad6fd1a0-71c4-4e47-8e13-fb82fcfcb224
myid-by-macro: 87e2f764-d28e-4981-94b8-ad3f05a6605a
The reason is that macro code are executed at comilation time, so after compilation this line
(def myid-by-macro (macro-random))
is equivalent to
(def myid-by-macro "87e2f764-d28e-4981-94b8-ad3f05a6605a")
You can even find the string value of myid-by-macro hardcoded in the compiled bytecode, try to unzip
playground.jar
and you'll find it inside a file namedplayground__init.class
.
And here is the complete source code:
(ns macrodemo.playground
(:gen-class)
(:import
java.util.UUID))
;; this value changes everytime you run your program
(def myid (str (UUID/randomUUID)))
(defmacro macro-random [] (str (UUID/randomUUID)))
;; this value is fixed for each compilation
(def myid-by-macro (macro-random))
(defn -main
[& _]
(dotimes [_ 2]
(println "myid:" myid)
(println "myid-by-macro:" myid-by-macro)))