I’m learning the Clojure and ClojureScript craft and I must say, I enjoy it. This article explains what leads me on the track of learning . This is the first part of my journey on this topic. Clojure/Script macro What’s the situation? To understand a programming language's value, it's generally relevant to detail a context of application of this feature. So here is the situation. Tiny study case I’m working on a web application project using ClojureScript. The application draws a diagram which can be seen as a graph: I keep track of nodes and the links between them. Nodes are rendered as bubbles (ellipses) and links as straight lines between bubbles. This information is stored in a , . Reagent is a ClojureScript library which makes the binding to React, the famous JavaScript library. Reagent atom appstate I’m a beginner, and setting up unit tests for my project was not top first priority. In the beginning I didn’t know what development environment to use (and you have the choice with Clojure/Script), how to connect things together, how to translate my ideas into code, what is the standard way of doing this or that, and so on… So I developed for a while without unit tests, I must confess. But with enough experience and confidence, at some point I felt it was the time for me to put in place some unit test around functions touching the application state. The following snippet shows you its skeleton: ( bubble.state ( [bubble.bubble bubble] [reagent.core reagent])) ( initial-application-state [] { [bubble/root-bubble] [] }) ( appstate ( ( ))) ns :require :as :as defn :bubbles :links defonce reagent/atom initial-application-state The atom is initialised by the function and it contains an hashmap: appstate initial-application-state the field is associated with a vector of bubbles (which are hashmaps themselves) :bubbles the field with a vector of couple of bubble ids :links Below the initialisation, you have a bench of functions to reate/ ead/ pdate/ elete it (not shown in snippet). So far so good. appstate C R U D By writing the first test, I realised that I didn’t like the way my functions were written. For example, let’s take a look at the function: add-bubble! ( add-bubble! [bubble] ( appstate update conj bubble)) defn swap! :bubbles As you can see, this function is straightforward: I call the function to modify the atom; swap! appstate I want to the contained hashmap; update Access to the field which contains a vector of bubbles; :bubbles Finally append (with the function) the input argument to this vector. conj bubble I like to write tiny functions, simple and straightforward, that do only one thing : sweet. But if you’re familiar with Functional Programming, maybe you noticed that uses the variable in its body; this variable is reachable because it’s a global variable declare ahead of . add-bubble! appstate add-bubble! Which means that the pretty cute function, it’s not a : oh my god! add-bubble! pure function On the other hand, it’s fairly common in web application to use (at least) one global variable to store the current state of the application. So is it that harmful to use not pure functions? Before dealing with this question, if you’re not (yet) aware of what a pure function is, well let’s clarify this term. Pure function: it’s not rocket science Daniel Higginbotham, author of the pedagogic, funny, well written, amazing (I could say more but I'll stop there) book , characterises pure functions as follows: Clojure for the Brave and True 1. It always returns the same result if given the same arguments. 2. It can’t cause any side effects. That’s it. This is the kind of function that you wrote when you learnt the basics of any programming language. the famous first example of function to write — is not a pure function as it performs I/O instruction: writing outputs to terminal. Nota Bene Hello World() — In our study case, is not a pure function as it reads/writes global variable. Well, my function is not a pure one, so what? Is it serious, doctor? add-bubble! appstate I would say it depends on the purpose of your project. It’s not the same if you’re trying to make a proof of concept or if you’re adding a feature to a large code base. In a short run, is a good candidat: it does the job, it’s readable, so nothing to declare. For me, the only downside is that this function cannot be tested elegantly. There are situations where test are not important: proofs of concept, technical investigations, projects which must be done in short slot of time, and so on. add-bubble! But in a long run, the multiplication of side effect functions touching a global state tends to make the maintainability more difficult. As it’s simple and seems harmless to read/write to a global entity from anywhere, the different parts of the code tend to get tangled to each other. Some dependencies between functions may exist implicitly, but these links can be hard to highlight. Globally, the project takes the direction of a big monolithic architecture. It becomes difficult to identify a piece of code which can be replace by a standard library which solves the same problem. This also makes the debugging part difficult. At some point, it would become systematic to execute one’s whole application to inspect a global variable state before and after the execution of a given function. In a such situation, the code tends to become more and more “ ” as one may say, which is not a good sign for its maintainability. write-only If you want to learn more about monolithic architecture, you can read . Monolithic vs. Microservices Architecture Another downside of side effect functions: they generally do not compose with each other. Composing functions is the ability to use the output of a function as the input of another one. If you ever used a in a *NIX terminal, this is a perfect example of program composition. pipe As Functional Programming is getting more and more attention, there are also a lot of articles and speaking about the benefits of using pure functions. These benefits are not restricted by any programming language; for example Ken Aguilar gives a . here there concrete study case in Javascript I my opinion, this regain of interest about Functional Programming is mainly due to these 2 points: maintainability and ability to compose functions. If you want to lean more about this topic, I recommend Eric Normand, teaching Clojure through his website , whom gives his opinion in purelyfunctional.tv Why is functional programming gaining traction? Why now? One thing I like the most about Functional Programming is that it allows me to evaluate the quality of a piece of code with more objective criteria. In this study case, the fact that is a short function and does one thing doesn't mean it is really convient in a long run. add-bubble! The first signs of this is the ability to be tested and its composability in my humble opinion. From my personal experience, a written code last more than I expected, so I prefer to use concepts which make it easier to maintain. Except for a school/study project, how often did you finish a project and then trash the source code away? And why did I want to use macro? With the early thought about how to test , its side effect aspect began to bit me: add-bubble! The global variable should be set and check respectively before and after the execution of . Compare to testing an output from a function, this way seems cumbersome; appstate add-bubble! What’s happen if I run the tests in a background process and simultaneously develop the code with a REPL session? Would the tests execution modify the current state of the application? I didn’t define what is a REPL session but quickly, REPL stands for ead- val- rint oop. It is the way that Clojure enhances the workflow development experience. It allows you to work with a quick feedback loop between the written code and its result after execution. R E P L If you want to learn more of this topic, the Clojure official documentation page is really what you need to read. Programming at the REPL: Introduction Anyway I didn’t want to spend time over these puzzling, uninteresting issues: setup unit test should be a simple task. With pure functions, all these questions simply disappear. After a refactoring, I ended up with this: ( add-bubble [appstate bubble] ( appstate conj bubble)) ( add-bubble! [bubble] ( appstate ( [appstate_arg] ( appstate_arg bubble)))) defn- update :bubbles defn swap! fn add-bubble One pure function and one function with side effect which uses the pure version of itself to update the global application state . It’s a common convention to put a “bang” (an exclamation mark) at the end of a function name if it does side effects in Clojure/Script world. add-bubble add-bubble! appstate Instead of testing directly, I could now test the function which takes the application state and other argument(s) as input, and return a new application state. add-bubble! add-bubble But with this solution, you would ask me: "Will you write two functions for each one which modify the application state in order to be able to test them easily?" I’m afraid to say: "Yes." This work is repetitive, error prone and boring, but the bang functions are necessary, easier to use in the other parts of the project. Despite the fact that I decided to do it this way, there is a good news: bang functions follow a simple pattern in their construction. I would like to write a code which takes a pure function as its input and generates the side effect version associated with it… Here we are: it can be done thanks to macros! Macros allow to generate code at compile time. The macro I need would take as input a pure function with an arbitrary arity (number of argument) and generated the side effect version of it. This is how my journey in learning Clojure/Script macro begins. This little use case gives me enough motivation to jump over the gap. Conclusion “What? That’s it? You didn’t tell anything about macro! Refund! Refund!” It’s not a trap, I swear. It’s my first article and I realised that the motivation part of the work (digressions involved) takes longer than what I thought. I finally preferred to split the story to get something more digest. However, I hope you learned something through this introduction. The second part of this journey is coming soon.