Threading expressions

Let's say we've got a list of strings from the database:

'("marion" "jake" "paula" "jani" "natasha")

(Note the symbol ' in front of the list. As mentioned before, we need it so that Clojure doesn't confuse the list with a function call. ' is actually a shorthand for function list.)

We need to process the list:

  1. Capitalize each string.
  2. Add a comma string between each string.
  3. Join all strings into one.

First, capitalizing can be done by applying capitalize to each element. We know that map can help with such tasks:

user=> (map capitalize 
	'("marion" "jake" "paula" "jani" "natasha"))

Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: capitalize in this context

Oh, snap! capitalize function isn't found. If you look into the documentation, you'll see that capitalize is located in clojure.string namespace. Namespaces are sort of like modules. We'll talk about namespaces later. For now, we only need one thing: a simple way to "import" a namespace into our current program:

user=> (require 'clojure.string)
nil

user=> (map clojure.string/capitalize 
	'("marion", "jake", "paula", "jani", "natasha"))

("Marion" "Jake" "Paula" "Jani" "Natasha")

Now is a good moment to talk about the user=> prompt. It indicates the current namespace. By default, you program lives in user. You can switch to another namespace or set a new one by using ns:

user=> (ns clojure.string)
nil

clojure.string=>

ns returned nil and now the prompt reflects the changed namespace. Once in clojure.string, we can use capitalize without a fully qualified name:

clojure.string=> (map capitalize 
	'("marion" "jake" "paula" "jani" "natasha"))

("Marion" "Jake" "Paula" "Jani" "Natasha")

We've been using lots of functions from clojure.core without explicitly requiring them or switching namespaces. That's because clojure.core along with some other namespaces are auto-required by Clojure for convenience.

Alright, let's get back into user namespace and continue. The 2nd task: add a comma between each string. clojure.core has a function interpose, which does exactly that: inserts the given element between elements of the given sequence.

user=> (interpose ", " (map clojure.string/capitalize 
	'("marion" "jake" "paula" "jani" "natasha")))

("Marion" ", " "Jake" ", " "Paula" ", " "Jani" ", " "Natasha")

And finally, join, which is another function from clojure.string:

user=> (clojure.string/join 
	(interpose ", " (map clojure.string/capitalize 
		'("marion" "jake" "paula" "jani" "natasha"))))
		
"Marion, Jake, Paula, Jani, Natasha"

Phew! It's a long line of code, and you need to read it from inside out, which isn't very comfortable at this scale. Clojure provides a helper threading macro ->>. (It has nothing to do with threads or processes; rather, it refers to threading through multiple things like when sewing fabrics.)

user=> (->> '("marion", "jake", "paula", "jani", "natasha")
            (map clojure.string/capitalize)
            (interpose ", ")
            (clojure.string/join))

"Marion, Jake, Paula, Jani, Natasha"

->> takes an expression and passes it as the last argument of the next expression. To pass it as the first argument, we'd use the -> macro instead.