Join Arthur Ulfeldt for an in-depth discussion in this video Exploring a few common macros, part of Up and Running with Clojure.
- Next I'd like to introduce a couple of macros that are found in the core library and that are very commonly used to make code just easier to read. I'll start with the Threading Macro. This is one of a whole family of macros designed to help make nested expressions in Clojure just plain easier to work with. So, all they do is take expressions that are nested and unwind them so they can be read from top to bottom. If we look at this expression on the left, and we trace it all the way to the inside, we can see we're starting by calling a with 1, we're passing the result of that as the first argument to calling b with a second argument of 2, and we're passing that as the first argument to a call to c where the second argument is 3, and the same for d.
You can piece that out if you sit down and stare at it, you can figure it out. This code on the right is exactly the same. In fact, it expands to the same thing, but it's simply read from top to bottom. This Threading Macro, this minus sign, greater than symbol -> here is pronounced Thread First. There is a whole variety of them Thread Last, Thread Coned. It's called Thread First because it places each result as the first argument to the next. So this says, make a of, calling a with 1 be the first argument to calling b with 2, and make that the first argument to calling c with 3, make that the first argument to calling d with 4 as the second argument, etc.
They do the same thing. Let's just prove it. Whenever you encounter a new macro in Clojure, it's always worthwhile to pop over to the repl, and macro expand it just to see what it does. So, here we can see that threading a to b to c to d results in a nested expression of a and b and c and d. Another very common one is the Cond Macro. This again, is part of a whole family of similarly related macros for helping make nested if statements look better.
So, on the left we have an opinionated function about temperature that checks to see if the temperature is negative, okay one result, if it's greater than some value, another result, if it's less than a different value, okay then some other result and then a default value if none of those apply. So we have if and if and if and if which you can puzzle out if work on it. You'll get back brisk. On the other side, we can use the Cond Macro and say, conditionally, if it's negative, it's that.
If this expression is true, it's that. If the temperature is less than 80, then it's that answer. Otherwise, it's that answer. So all this does is translate in to a series of if statements that place the value in the left column as the if condition, the value in the right column as the answer if that if was true, and if not, it goes on to the next one. One little note. This default down here, isn't really a special keyword. It's just that all keywords are truthy enClojure.
Remember I mentioned if statements, check to see if their value is truthy or not truthy? Where the falsey values are nil and false. Default is a keyword. It is neither nil nor false, so it is true.
Arthur Ulfeldt covers the Leiningen build tool and setting up Clojure to work with the IntelliJ IDEA dev environment. He then reviews the basics of the syntax, including functions, expressions, values, macros, strings, and conditionals. He shows how to structure, compile, and deploy Clojure projects in Leiningen, and pull from Clojure's core library. In the final chapters, Arthur explores references and namespaces and points to resources to learn more about Clojure.
- Installing Leiningen
- Configuring IntelliJ IDEA
- Using REPLs to execute code
- Working with simple and composite values
- Mastering Clojure macros
- Exploring Clojure syntax
- Building a Clojure project with Leiningen
- Mapping, filtering, and reducing
- Binding and destructuring data
- Working with identities