Top Banner
39

Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat...

Jul 31, 2018

Download

Documents

truongnhan
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02
Page 2: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Clojure for the Brave and True

Daniel Higginbotham

This book is for sale at http://leanpub.com/clojure-for-the-brave-and-true

This version was published on 2015-11-02

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishingprocess. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools andmany iterations to get reader feedback, pivot until you have the right book and build traction onceyou do.

©2013 - 2015 Daniel Higginbotham

Page 3: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Contents

Do Things: a Clojure Crash Course . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1Data Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14Pulling It All Together . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26What Now? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

Page 4: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash CourseIt’s time to to learn how to actually do things with Clojure! Hot damn!

While you’ve undoubtedly heard of Clojure’s awesome concurrency support and other stupendousfeatures, Clojure’s most salient characteristic is that it is a Lisp. In this chapter, you’re going toexplore the elements which comprise this Lisp core: syntax, functions, and data. This will provideyou with a solid foundation for representing and solving problems in Clojure.

This groundwork will also allow you to write some super important code. In the last section, you’lltie everything together by creating a model of a hobbit and writing a function to hit it in a randomspot. Super! Important!

As you go through the chapter, I recommend that you type out the examples in a REPL and runthem. Programming in a new language is a skill, and, just like yodeling or synchronized swimming,you have to practice it to learn it. By the way, “Synchronized Swimming for Yodelers for the Braveand True” is due to be published in August of 20never. Check it out!

Syntax

Clojure’s syntax is simple. Like all Lisps, it employs a uniform structure, a handful of specialoperators, and a constant supply of parentheses delivered from the parenthesis mines hidden beneaththe Massachusetts Institute of Technology, where Lisp was born.

Forms

All Clojure code is written in a uniform structure. Clojure understands:

1. Literal representations of data structures like numbers, strings, maps, and vectors2. Operations

We use the term form to refer to structurally valid code. These literal representations are all validforms:

1 1

2 "a string"

3 ["a" "vector" "of" "strings"]

Your codewill rarely contain free-floating literals, of course, since they don’t actually do anything ontheir own. Instead, you’ll use literals in operations. Operations are how you do things. All operationstake the form, “opening parthensis, operator, operands, closing parenthesis”:

Page 5: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 2

1 (operator operand1 operand2 ... operandn)

Notice that there are no commas. Clojure uses whitespace to separate operands and it treats commasas whitespace. Here are some example operations:

1 (+ 1 2 3)

2 ; => 6

3

4 (str "It was the panda " "in the library " "with a dust buster")

5 ; => "It was the panda in the library with a dust buster"

To recap, Clojure consists of forms. Forms have a uniform structure. They consist of literals andoperations. Operations consist of forms enclosed within parentheses.

For good measure, here’s something that is not a form because it doesn’t have a closing parenthesis:

1 (+

Clojure’s structural uniformity is probably different from what you’re used to. In other languages,different operations might have different structures depending on the operator and the operands.For example, JavaScript employs a smorgasbord of infix notation, dot operators, and parentheses:

1 1 + 2 + 3

2 "It was the panda ".concat("in the library ", "with a dust buster")

Clojure’s structure is very simple and consistent by comparison. No matter what operator you’reusing or what kind of data you’re operating on, the structure is the same.

One final note: I’ll also use the term expression to refer to Clojure forms. Don’t get too hung up onthe terminology, though.

Control Flow

Here are some basic control flow operators. Throughout the book you’ll encounter more.

if

The general structure of if is:

Page 6: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 3

1 (if boolean-form

2 then-form

3 optional-else-form)

Here’s an example:

1 (if true

2 "abra cadabra"

3 "hocus pocus")

4 ; => "abra cadabra"

Notice that each branch of the if can only have one form. This is different from most languages. Forexample, in Ruby you can write:

1 if true

2 doer.do_thing(1)

3 doer.do_thing(2)

4 else

5 other_doer.do_thing(1)

6 other_doer.do_thing(2)

7 end

To get around this apparent limitation, we have the do operator:

do

do lets you “wrap up” multiple forms. Try the following in your REPL:

1 (if true

2 (do (println "Success!")

3 "abra cadabra")

4 (do (println "Failure :(")

5 "hocus pocus"))

6 ; => Success!

7 ; => "abra cadabra"

In this case, Success! is printed in the REPL and "abra cadabra" is returned as the value of theentire if expression.

when

The when operator is like a combination of if and do, but with no else form. Here’s an example:

Page 7: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 4

1 (when true

2 (println "Success!")

3 "abra cadabra")

4 ; => Success!

5 ; => "abra cadabra"

Use when when you want to do multiple things when some condition is true, and you don’t want todo anything when the condition is false.

That covers the essential control flow operators!

Naming Things with def

One final thing before we move on to data structures: you use def to bind a name to a value inClojure:

1 (def failed-protagonist-names

2 ["Larry Potter"

3 "Doreen the Explorer"

4 "The Incredible Bulk"])

In this case, you’re binding the name failed-protagonist-names to a vector containing threestrings. Notice that I’m using the term “bind”, whereas in other langauges you’d say that you’reassigning a value to a variable. For example, in Ruby you might perform multiple assignments to avariable to “build up” its value:

1 severity = :mild

2 error_message = "OH GOD! IT'S A DISASTER! WE'RE "

3 if severity == :mild

4 error_message = error_message + "MILDLY INCONVENIENCED!"

5 else

6 error_message = error_message + "DOOOOOOOMED!"

7 end

The Clojure equivalent would be:

Page 8: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 5

1 (def severity :mild)

2 (def error-message "OH GOD! IT'S A DISASTER! WE'RE ")

3 (if (= severity :mild)

4 (def error-message (str error-message "MILDLY INCONVENIENCED!"))

5 (def error-message (str error-message "DOOOOOOOMED!")))

However, this is really bad Clojure. For now, you should treat def as if it’s defining constants. Butfear not! Over the next few chapters you’ll learn how to work with this apparent limitation by codingin the functional style.

Data Structures

Clojure comes with a handful of data structures which you’ll find yourself using the majority of thetime. If you’re coming from an object-oriented background, you’ll be surprised at how much youcan do with the “basic” types presented here.

All of Clojure’s data structures are immutable, meaning you can’t change them in place. There’s noClojure equivalent for the following Ruby:

1 failed_protagonist_names = [

2 "Larry Potter",

3 "Doreen the Explorer",

4 "The Incredible Bulk"

5 ]

6 failed_protagonist_names[0] = "Gary Potter"

7 failed_protagonist_names

8 # => [

9 # "Gary Potter",

10 # "Doreen the Explorer",

11 # "The Incredible Bulk"

12 # ]

You’ll learn more about why Clojure was implemented this way, but for now it’s fun to just learnhow to do things without all that philosophizing. Without further ado:

nil, true, false, Truthiness, Equality

Clojure has true and false values. nil is used to indicate “no value” in Clojure. You can check if avalue is nil with the cleverly named nil? function:

Page 9: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 6

1 (nil? 1)

2 ; => false

3

4 (nil? nil)

5 ; => true

Both nil and false are used to represent logical falsiness, while all other values are logically truthy.= is the equality operator:

1 (= 1 1)

2 ; => true

3

4 (= nil nil)

5 ; => true

6

7 (= 1 2)

8 ; => false

Some other languages require you to use different operators when comparing values of differenttypes. For example, you might have to use some kind of special “string equality” operator speciallymade just for strings. You don’t need anything weird or tedious like that to test for equality whenusing Clojure’s built-in data structures.

Numbers

Clojure has pretty sophisticated numerical support. I’m not going to spend much time dwelling onthe boring technical details (like coercion and contagion), because that will get in the way of doingthings. If you’re interested in said boring details, check out http://clojure.org/data_structures#DataStructures-Numbers¹. Suffice to say that Clojure will merrily handle pretty much anything youthrow at it.

In the mean time, we’ll be working with integers and floats. We’ll also be working with ratios, whichClojure can represent directly. Here’s an integer, a float, and a ratio:

1 93

2 1.2

3 1/5

Strings

Here are some string examples:

¹http://clojure.org/data_structures#DataStructures-Numbers

Page 10: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 7

1 "Lord Voldemort"

2 "\"He who must not be named\""

3 "\"Great cow of Moscow!\" - Hermes Conrad"

Notice that Clojure only allows double quotes to delineate strings. 'Lord Voldemort', for example,is not a valid string. Also notice that Clojure doesn’t have string interpolation. It only allowsconcatenation via the str function:

1 (def name "Chewbacca")

2 (str "\"Uggllglglglglglglglll\" - " name)

3 ; => "Uggllglglglglglglglll" - Chewbacca

Maps

Maps are similar to dictionaries or hashes in other languages. They’re a way of associating somevalue with some other value. Here are example map literals:

1 ;; An empty map

2 {}

3

4 ;; ":a", ":b", ":c" are keywords and we'll cover them in the next section

5 {:a 1

6 :b "boring example"

7 :c []}

8

9 ;; Associate "string-key" with the "plus" function

10 {"string-key" +}

11

12 ;; Maps can be nested

13 {:name {:first "John" :middle "Jacob" :last "Jingleheimerschmidt"}}

Notice that map values can be of any type. String, number, map, vector, even function! Clojure don’tcare!

You can look up values in maps with the get function:

Page 11: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 8

1 (get {:a 0 :b 1} :b)

2 ; => 1

3

4 (get {:a 0 :b {:c "ho hum"}} :b)

5 ; => {:c "ho hum"}

get will return nil if it doesn’t find your key, but you can give it a default value to return:

1 (get {:a 0 :b 1} :c)

2 ; => nil

3

4 (get {:a 0 :b 1} :c "UNICORNS")

5 ; => "UNICORNS"

The get-in function lets you look up values in nested maps:

1 (get-in {:a 0 :b {:c "ho hum"}} [:b :c])

2 ; => "ho hum"

[:b :c] is a vector, which you’ll read about in a minute.

Another way to look up a value in a map is to treat the map like a function, with the key as itsargument:

1 ({:name "The Human Coffee Pot"} :name)

2 ; => "The Human Coffee Pot"

Real Clojurists hardly ever do this, though. However, Real Clojurists do use keywords to look upvalues in maps:

Keywords

Clojure keywords are best understood by the way they’re used. They’re primarily used as keys inmaps, as you can see above. Examples of keywords:

1 :a

2 :rumplestiltsken

3 :34

4 :_?

Keywords can be used as functions which look up the corresponding value in a data structure. Forexample:

Page 12: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 9

1 ;; Look up :a in map

2 (:a {:a 1 :b 2 :c 3})

3 ; => 1

4

5 ;; This is equivalent to:

6 (get {:a 1 :b 2 :c 3} :a)

7 ; => 1

8

9 ;; Provide a default value, just like get:

10 (:d {:a 1 :b 2 :c 3} "FAERIES")

11 ; => "FAERIES"

I think this is super cool and Real Clojurists do it all the time. You should do it, too!

Besides using map literals, you can use the hash-map function to create a map:

1 (hash-map :a 1 :b 2)

2 ; => {:a 1 :b 2}

Clojure also lets you create sorted maps, but I won’t be covering that.

Vectors

A vector is similar to an array in that it’s a 0-indexed collection:

1 ;; Here's a vector literal

2 [3 2 1]

3

4 ;; Here we're returning an element of a vector

5 (get [3 2 1] 0)

6 ; => 3

7

8 ;; Another example of getting by index. Notice as well that vector

9 ;; elements can be of any type and you can mix types.

10 (get ["a" {:name "Pugsley Winterbottom"} "c"] 1)

11 ; => {:name "Pugsley Winterbottom"}

Notice that we’re using the same get function as we use when looking up values in maps. The nextchapter explains why we do this.

You can create vectors with the vector function:

Page 13: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 10

1 (vector "creepy" "full" "moon")

2 ; => ["creepy" "full" "moon"]

Elements get added to the end of a vector:

1 (conj [1 2 3] 4)

2 ; => [1 2 3 4]

Lists

Lists are similar to vectors in that they’re linear collections of values. There are some differences,though. You can’t retrieve list elements with get:

1 ;; Here's a list - note the preceding single quote

2 '(1 2 3 4)

3 ; => (1 2 3 4)

4 ;; Notice that the REPL prints the list without a quote. This is OK,

5 ;; and it'll be explained later.

6

7

8 ;; Doesn't work for lists

9 (get '(100 200 300 400) 0)

10

11 ;; This works but has different performance characteristics which we

12 ;; don't care about right now.

13 (nth '(100 200 300 400) 3)

14 ; => 400

You can create lists with the list function:

1 (list 1 2 3 4)

2 ; => (1 2 3 4)

Elements get added to the beginning of a list:

1 (conj '(1 2 3) 4)

2 ; => (4 1 2 3)

When should you use a list and when should you use a vector? For now, you’re probably best offjust using vectors. As you learn more, you’ll get a good feel for when to use which.

Sets

Sets are collections of unique values:

Page 14: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 11

1 ;; Literal notation

2 #{"hannah montanna" "miley cyrus" 20 45}

3

4 ;; If you try to add :b to a set which already contains :b,

5 ;; the set still only has one :b

6 (conj #{:a :b} :b)

7 ; => #{:a :b}

8

9 ;; You can check whether a value exists in a set

10 (get #{:a :b} :a)

11 ; => :a

12

13 (:a #{:a :b})

14 ; => :a

15

16 (get #{:a :b} "hannah montanna")

17 ; => nil

You can create sets from existing vectors and lists by using the set function. One unobvious use forthis is to check whether an element exists in a collection:

1 (set [3 3 3 4 4])

2 ; => #{3 4}

3

4 ;; 3 exists in vector

5 (get (set [3 3 3 4 4]) 3)

6 ; => 3

7

8 ;; but 5 doesn't

9 (get (set [3 3 3 4 4]) 5)

10 ; => nil

Just as you can create hash maps and sorted maps, you can create hash sets and sorted sets:

1 (hash-set 1 1 3 1 2)

2 ; => #{1 2 3}

3

4 (sorted-set :b :a :c)

5 ; => #{:a :b :c}

Clojure also lets you define how a set is sorted using the sorted-set-by function, but this bookdoesn’t cover that.

Page 15: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 12

Symbols and Naming

Symbols are identifiers that are normally used to refer to something. Let’s look at a def example:

1 (def failed-movie-titles ["Gone With the Moving Air" "Swellfellas"])

In this case, def associates the value ["Gone With the Moving Air" "Swellfellas"] with thesymbol failed-movie-titles.

You might be thinking, “So what? Every other programming language lets me associate a namewith a value. Big whoop!” Lisps, however, allow you to manipulate symbols as data, somethingwe’ll see a lot of when we start working with macros. Functions can return symbols and take themas arguments:

1 ;; Identity returns its argument

2 (identity 'test)

3 ; => test

For now, though, it’s OK to think “Big whoop!” and not be very impressed.

Quoting

Youmay have noticed the single quote, ', in the examples above. This is called “quoting”. You’ll learnabout this in detail in the chapter “Clojure Alchemy: Reading, Evaluation, and Macros”. Here’s thequick explanation for now.

Giving Clojure a symbol returns the “object” it refers to:

1 failed-protagonist-names

2 ; => ["Larry Potter" "Doreen the Explorer" "The Incredible Bulk"]

3

4 (first failed-protagonist-names)

5 ; => "Larry Potter"

Quoting a symbol tells Clojure to use the symbol itself as a data structure, not the object the symbolrefers to:

Page 16: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 13

1 'failed-protagonist-names

2 ; => failed-protagonist-names

3

4 (eval 'failed-protagonist-names)

5 ; => ["Larry Potter" "Doreen the Explorer" "The Incredible Bulk"]

6

7 (first 'failed-protagonist-names)

8 ; => Throws exception!

9

10 (first ['failed-protagonist-names 'failed-antagonist-names])

11 ; => failed-protagonist-names

You can also quote collections like lists, maps, and vectors. All symbols within the collection will beunevaluated:

1 '(failed-protagonist-names 0 1)

2 ; => (failed-protagonist-names 0 1)

3

4 (first '(failed-protagonist-names 0 1))

5 ; => failed-protagonist-names

6

7 (second '(failed-protagonist-names 0 1))

8 ; => 0

Simplicity

You may have noticed that this treatment of data structures doesn’t include a description of howto create new types or classes. This is because Clojure’s emphasis on simplicity encourages you toreach for the built-in, “basic” data structures first.

If you come from an object-oriented background, you might think that this approach is weird andbackwards. What you’ll find, though, is that your data does not have to be tightly bundled with aclass for it to be useful and intelligible. Here’s an epigram loved by Clojurists which hints at theClojure philosophy:

1 It is better to have 100 functions operate on one data structure

2 than 10 functions on 10 data structures.

3

4 -- Alan Perlis

You’ll learn more about this aspect of Clojure’s philosophy in the coming chapters. For now, though,keep an eye out for the ways that you gain code re-use by sticking to basic data structures.

Thus concludes our Clojure data structures primer. Now it’s time to dig in to functions and see howthese data structures can be used!

Page 17: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 14

Functions

One of the reasons people go nuts over Lisps is that they allow you to build programs which behavein complex ways, yet the primary building block — the function — is so simple. This section willinitiate you in the beauty and elegance of Lisp functions by explaining:

• Calling functions• How functions differ from macros and special forms• Defining functions• Anonymous functions• Returning functions

Calling Functions

By now you’ve seen many examples of function calls:

1 (+ 1 2 3 4)

2 (* 1 2 3 4)

3 (first [1 2 3 4])

I’ve already gone over how all Clojure expressions have the same syntax: opening parenthesis,operator, operands, closing parenthesis. “Function call” is just another term for an expression wherethe operator is a function expression. A function expression is just an expression which returns afunction.

It might not be obvious, but this lets you write some pretty interesting code. Here’s a functionexpression which returns the + (addition) function:

1 ;; Return value of "or" is first truthy value, and + is truthy

2 (or + -)

You can use that expression as the operator in another expression:

1 ((or + -) 1 2 3)

2 ; => 6

Here are a couple more valid function calls which return 6:

Page 18: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 15

1 ;; Return value of "and" is first falsey value or last truthy value.

2 ;; + is the last truthy value

3 ((and (= 1 1) +) 1 2 3)

4

5 ;; Return value of "first" is the first element in a sequence

6 ((first [+ 0]) 1 2 3)

However, these aren’t valid function calls:

1 ;; Numbers aren't functions

2 (1 2 3 4)

3

4 ;; Neither are strings

5 ("test" 1 2 3)

If you run these in your REPL you’ll get something like

1 ClassCastException java.lang.String cannot be cast to clojure.lang.IFn

2 user/eval728 (NO_SOURCE_FILE:1)

You’re likely to see this error many times as you continue with Clojure. “x cannot be cast toclojure.lang.IFn” just means that you’re trying something as a function when it’s not.

Function flexibility doesn’t end with the function expression! Syntactically, functions can take anyexpressions as arguments — including other functions.

Take the map function (not to be confused with the map data structure). map creates a new list byapplying a function to each member of a collection:

1 ;; The "inc" function increments a number by 1

2 (inc 1.1)

3 ; => 2.1

4

5 (map inc [0 1 2 3])

6 ; => (1 2 3 4)

(Note that map doesn’t return a vector even though we supplied a vector as an argument. You’ll learnwhy later. For now, just trust that this is OK and expected.)

Indeed, Clojure’s ability to receive functions as arguments allows you to build more powerfulabstractions. Those unfamiliar with this kind of programming think of functions as allowing you togeneralize operations over data instances. For example, the + function abstracts addition over anyspecific numbers.

Page 19: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 16

By contrast, Clojure (and all Lisps) allows you to create functions which generalize over processes.map allows you to generalize the process of transforming a collection by applying a function — anyfunction — over any collection.

The last thing that you need know about function calls is that Clojure evaluates all functionarguments recursively before passing them to the function. Here’s how Clojure would evaluate afunction call whose arguments are also function calls:

1 ;; Here's the function call. It kicks off the evaluation process

2 (+ (inc 199) (/ 100 (- 7 2)))

3

4 ;; All sub-forms are evaluated before applying the "+" function

5 (+ 200 (/ 100 (- 7 2))) ; evaluated "(inc 199)"

6 (+ 200 (/ 100 5)) ; evaluated (- 7 2)

7 (+ 200 20) ; evaluated (/ 100 5)

8 220 ; final evaluation

Function Calls, Macro Calls, and Special Forms

In the last section, you learned that function calls are expressions which have a function expressionas the operator. There are two other kinds of expressions: macro calls and special forms. You’vealready seen a couple special forms:

1 (def failed-movie-titles ["Gone With the Moving Air" "Swellfellas"])

2 (if (= severity :mild)

3 (def error-message (str error-message "MILDLY INCONVENIENCED!"))

4 (def error-message (str error-message "DOOOOOOOMED!")))

You’ll learn everything there is to know about macro calls and special forms in the chapter “ClojureAlchemy: Reading, Evaluation, andMacros”. For now, though, the main feature which makes specialforms “special” is that they don’t always evaluate all of their operands, unlike function calls.

Take if, for example. Its general structure is:

1 (if boolean-form

2 then-form

3 optional-else-form)

Now imagine you had an if statement like this:

Page 20: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 17

1 (if good-mood

2 (tweet walking-on-sunshine-lyrics)

3 (tweet mopey-country-song-lyrics))

If Clojure evaluated both tweet function calls, then your followers would end up very confused.

Another feature which differentiates special forms is that you can’t use them as arguments tofunctions.

In general, special forms implement core Clojure functionality that just can’t be implemented withfunctions. There are only a handful of Clojure special forms, and it’s pretty amazing that such a richlanguage is implemented with such a small set of building blocks.

Macros are similar to special forms in that they evaluate their operands differently from functioncalls and they also can’t be passed as arguments to functions. But this detour has taken long enough;it’s time to learn how to define functions!

Defining Functions

Function definitions are comprised of five main parts:

• defn

• A name• (Optional) a docstring• Parameters• The function body

Here’s an example of a function definition and calling the function:

1 (defn too-enthusiastic

2 "Return a cheer that might be a bit too enthusiastic"

3 [name]

4 (str "OH. MY. GOD! " name " YOU ARE MOST DEFINITELY LIKE THE BEST "

5 "MAN SLASH WOMAN EVER I LOVE YOU AND WE SHOULD RUN AWAY TO SOMEWHERE"))

6

7 (too-enthusiastic "Zelda")

8 ; => "OH. MY. GOD! Zelda YOU ARE MOST DEFINITELY LIKE THE BEST MAN SLASH WOMAN E\

9 VER I LOVE YOU AND WE SHOULD RUN AWAY TO SOMEWHERE"

Let’s dive deeper into the docstring, parameters, and function body.

Page 21: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 18

The Docstring

The docstring is really cool. You can view the docstring for a function in the REPL with (doc fn-

name), e.g. (doc map). The docstring is also utilized if you use a tool to generate documentation foryour code. In the above example, "Return a cheer that might be a bit too enthusiastic" isthe docstring.

Parameters

Clojure functions can be defined with zero or more parameters:

1 (defn no-params

2 []

3 "I take no parameters!")

4

5 (defn one-param

6 [x]

7 (str "I take one param: " x " It'd better be a string!"))

8

9 (defn two-params

10 [x y]

11 (str "Two parameters! That's nothing! Pah! I will smoosh them "

12 "together to spite you! " x y))

Functions can also be overloaded by arity. This means that a different function body will rundepending on the number of arguments passed to a function.

Here’s the general form of a multiple-arity function definition. Notice that each arity definition isenclosed in parentheses and has an argument list:

1 (defn multi-arity

2 ;; 3-arity arguments and body

3 ([first-arg second-arg third-arg]

4 (do-things first-arg second-arg third-arg))

5 ;; 2-arity arguments and body

6 ([first-arg second-arg]

7 (do-things first-arg second-arg))

8 ;; 1-arity arguments and body

9 ([first-arg]

10 (do-things first-arg)))

Overloading by arity is one way to provide default values for arguments. In this case, "karate" isthe default argument for the chop-type param:

Page 22: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 19

1 (defn x-chop

2 "Describe the kind of chop you're inflicting on someone"

3 ([name chop-type]

4 (str "I " chop-type " chop " name "! Take that!"))

5 ([name]

6 (x-chop name "karate")))

If you call x-chop with two arguments, then the function works just as it would if it weren’t amulti-arity function:

1 (x-chop "Kanye West" "slap")

2 ; => "I slap chop Kanye West! Take that!"

If you call x-chop with only one argument, though, then x-chop will actually call itself with thesecond argument "karate" supplied:

1 (x-chop "Kanye East")

2 ; => "I karate chop Kanye East! Take that!"

It might seem unusual to define a function in terms of itself like this. If so, great! You’re learning anew way to do things!

You can also make each arity do something completely unrelated:

1 (defn weird-arity

2 ([]

3 "Destiny dressed you this morning my friend, and now Fear is

4 trying to pull off your pants. If you give up, if you give in,

5 you're gonna end up naked with Fear just standing there laughing

6 at your dangling unmentionables! - the Tick")

7 ([number]

8 (inc number)))

But most likely, you don’t want to do that.

Clojure also allows you to define variable-arity functions by including a “rest-param”, as in “put therest of these arguments in a list with the following name”:

Page 23: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 20

1 (defn codger-communication

2 [whippersnapper]

3 (str "Get off my lawn, " whippersnapper "!!!"))

4

5 (defn codger

6 [& whippersnappers] ;; the ampersand indicates the "rest-param"

7 (map codger-communication whippersnappers))

8

9 (codger "Billy" "Anne-Marie" "The Incredible Bulk")

10 ; =>

11 ; ("Get off my lawn, Billy!!!"

12 ; "Get off my lawn, Anne-Marie!!!"

13 ; "Get off my lawn, The Incredible Bulk!!!")

As you can see, when you provide arguments to variable-arity functions, the arguments get treatedas a list.

You can mix rest-params with normal params, but the rest-param has to come last:

1 (defn favorite-things

2 [name & things]

3 (str "Hi, " name ", here are my favorite things: "

4 (clojure.string/join ", " things)))

5

6 (favorite-things "Doreen" "gum" "shoes" "kara-te")

7 ; => "Hi, Doreen, here are my favorite things: gum, shoes, kara-te"

Finally, Clojure has a more sophisticated way of defining parameters called “destructuring”, whichdeserves its own subsection:

Destructuring

The basic idea behind destructuring is that it lets you concisely bind symbols to values within acollection. Let’s look at a basic example:

Page 24: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 21

1 ;; Return the first element of a collection

2 (defn my-first

3 [[first-thing]] ; Notice that first-thing is within a vector

4 first-thing)

5

6 (my-first ["oven" "bike" "waraxe"])

7 ; => "oven"

Here’s how you would accomplish the same thing without destructuring:

1 (defn my-other-first

2 [collection]

3 (first collection))

4 (my-other-first ["nickel" "hair"])

5 ; => "nickel"

As you can see, the my-first associates the symbol first-thingwith the first element of the vectorthat was passed in as an argument. You tell my-first to do this by placing the symbol first-thingwithin a vector.

That vector is like a huge sign held up to Clojure which says, “Hey! This function is going to receive alist or a vector or a set as an argument. Make my life easier by taking apart the argument’s structurefor me and associating meaningful names with different parts of the argument!”

When destructuring a vector or list, you can name as many elements as you want and also use restparams:

1 (defn chooser

2 [[first-choice second-choice & unimportant-choices]]

3 (println (str "Your first choice is: " first-choice))

4 (println (str "Your second choice is: " second-choice))

5 (println (str "We're ignoring the rest of your choices. "

6 "Here they are in case you need to cry over them: "

7 (clojure.string/join ", " unimportant-choices))))

8 (chooser ["Marmalade", "Handsome Jack", "Pigpen", "Aquaman"])

9 ; =>

10 ; Your first choice is: Marmalade

11 ; Your second choice is: Handsome Jack

12 ; We're ignoring the rest of your choices. Here they are in case \

13 ; you need to cry over them: Pigpen, Aquaman

You can also destructure maps. In the same way that you tell Clojure to destructure a vector or listby providing a vector as a parameter, you destucture maps by providing a map as a parameter:

Page 25: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 22

1 (defn announce-treasure-location

2 [{lat :lat lng :lng}]

3 (println (str "Treasure lat: " lat))

4 (println (str "Treasure lng: " lng)))

5 (announce-treasure-location {:lat 28.22 :lng 81.33})

6 ; =>

7 ; Treasure lat: 28.22

8 ; Treasure lng: 81.33

Let’s look more at this line:

1 [{lat :lat lng :lng}]

This is like telling Clojure, “Yo! Clojure! Do me a flava and associate the symbol lat with the valuecorresponding to the key :lat. Do the same thing with lng and :lng, ok?.”

We often want to just take keywords and “break them out” of a map, so there’s a shorter syntax forthat:

1 ;; Works the same as above.

2 (defn announce-treasure-location

3 [{:keys [lat lng]}]

4 (println (str "Treasure lat: " lat))

5 (println (str "Treasure lng: " lng)))

You can retain access to the original map argument by using the :as keyword. In the example below,the original map is accessed with treasure-location:

1 ;; Works the same as above.

2 (defn receive-treasure-location

3 [{:keys [lat lng] :as treasure-location}]

4 (println (str "Treasure lat: " lat))

5 (println (str "Treasure lng: " lng))

6

7 ;; One would assume that this would put in new coordinates for your ship

8 (steer-ship! treasure-location))

In general, you can think of destructuring as instructing Clojure how to associate symbols withvalues in a list, map, set, or vector.

Now, on to the part of the function that actually does something: the function body!

Function body

Your function body can contain any forms. Clojure automatically returns the last form evaluated:

Page 26: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 23

1 (defn illustrative-function

2 []

3 (+ 1 304)

4 30

5 "joe")

6 (illustrative-function)

7 ; => "joe"

8

9 (defn number-comment

10 [x]

11 (if (> x 6)

12 "Oh my gosh! What a big number!"

13 "That number's OK, I guess"))

14

15 (number-comment 5)

16 ; => "That number's OK, I guess"

17

18 (number-comment 7)

19 ; => "Oh my gosh! What a big number!"

All Functions are Created Equal

One final note: in Clojure, there are no privileged functions. + is just a function, - is just a function,inc and map are just functions. They’re no better than your functions! So don’t let them give youany lip.

More importantly, this fact helps to demonstrate Clojure’s underlying simplicity. In a way, Clojureis very dumb. When you make a function call, Clojure just says, “map? Sure, whatever! I’ll just applythis andmove on.” It doesn’t care what the function is or where it came from, it treats all functions thesame. At its core, Clojure doesn’t give two burger flips about addition, multiplication, or mapping.It just cares about applying functions.

As you program with Clojure more, you’ll see that this simplicity is great. You don’t have to worryabout special rules or syntax for working with functions. They all work the same!

Anonymous Functions

In Clojure, your functions don’t have to have names. In fact, you’ll find yourself using anonymousfunctions all the time. How mysterious!

There are two ways to create anonymous functions. The first is to use the fn form:

Page 27: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 24

1 ;; This looks a lot like defn, doesn't it?

2 (fn [param-list]

3 function body)

4

5 ;; Example

6 (map (fn [name] (str "Hi, " name))

7 ["Darth Vader" "Mr. Magoo"])

8 ; => ("Hi, Darth Vader" "Hi, Mr. Magoo")

9

10 ;; Another example

11 ((fn [x] (* x 3)) 8)

12 ; => 24

You can treat fn nearly identically to the way you treat defn. The parameter lists and function bodieswork exactly the same. You can use argument destructuring, rest-params, and so on.

You could even associate your anonymous functionwith a name, which shouldn’t come as a surprise:

1 (def my-special-multiplier (fn [x] (* x 3)))

2 (my-special-multiplier 12)

3 ; => 36

(If it does come as a surprise, then… Surprise!)

There’s another, more compact way to create anonymous functions:

1 ;; Whoa this looks weird.

2 #(* % 3)

3

4 ;; Apply this weird looking thing

5 (#(* % 3) 8)

6 ; => 24

7

8 ;; Another example

9 (map #(str "Hi, " %)

10 ["Darth Vader" "Mr. Magoo"])

11 ; => ("Hi, Darth Vader" "Hi, Mr. Magoo")

You can see that it’s definitely more compact, but it’s probably also confusing. Let’s break it down.

This kind of anonymous function looks a lot like a function call, except that it’s preceded by a poundsign, #:

Page 28: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 25

1 ;; Function call

2 (* 8 3)

3

4 ;; Anonymous function

5 #(* % 3)

This similarity allows you to more quickly see what will happen when this anonymous functiongets applied. “Oh,” you can say to yourself, “this is going to multiply its argument by 3”.

As you may have guessed by now, the percent sign, %, indicates the argument passed to the function.If your anonymous function takes multiple arguments, you can distinguish them like this: %1, %2, %3,etc. % is equivalent to %1:

1 (#(str %1 " and " %2) "corn bread" "butter beans")

2 ; => "corn bread and butter beans"

You can also pass a rest param:

1 (#(identity %&) 1 "blarg" :yip)

2 ; => (1 "blarg" :yip)

The main difference between this form and fn is that this form can easily become unreadable andis best used for short functions.

Returning Functions

Functions can return other functions. The returned functions are closures, which means that theycan access all the variables that were in scope when the function was created.

Here’s a standard example:

1 ;; inc-by is in scope, so the returned function has access to it even

2 ;; when the returned function is used outside inc-maker

3 (defn inc-maker

4 "Create a custom incrementor"

5 [inc-by]

6 #(+ % inc-by))

7

8 (def inc3 (inc-maker 3))

9

10 (inc3 7)

11 ; => 10

Woohoo!

Page 29: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 26

Pulling It All Together

OK! Let’s pull all this together and use our knowledge for a noble purpose: smacking around hobbits!

In order to hit a hobbit, we’ll first model its body parts. Each body part will include its relative sizeto help us determine how likely it is that that part will be hit.

In order to avoid repetition, this hobbit model will only include entries for “left foot”, “left ear”, etc.Therefore, we’ll need a function to fully symmetrize the model.

Finally, we’ll create a function which iterates over our body parts and randomly chooses the one hit.

Fun!

The Shire’s Next Top Model

For our hobbit model, we’ll eschew such characteristics as “joviality” and “mischievousness” andfocus only on the hobbit’s tiny body. Here’s our hobbit model:

1 (def asym-hobbit-body-parts [{:name "head" :size 3}

2 {:name "left-eye" :size 1}

3 {:name "left-ear" :size 1}

4 {:name "mouth" :size 1}

5 {:name "nose" :size 1}

6 {:name "neck" :size 2}

7 {:name "left-shoulder" :size 3}

8 {:name "left-upper-arm" :size 3}

9 {:name "chest" :size 10}

10 {:name "back" :size 10}

11 {:name "left-forearm" :size 3}

12 {:name "abdomen" :size 6}

13 {:name "left-kidney" :size 1}

14 {:name "left-hand" :size 2}

15 {:name "left-knee" :size 2}

16 {:name "left-thigh" :size 4}

17 {:name "left-lower-leg" :size 3}

18 {:name "left-achilles" :size 1}

19 {:name "left-foot" :size 2}])

This is a vector of maps. Each map has the name of the body part and relative size of the body part.Look, I know that only anime characters have eyes 1/3 the size of their head, but just go with it, OK?

Conspicuously missing is the hobbit’s right side. Let’s fix that. The code below is the most complexcode we’ve looked at so far. It introduces some ideas we haven’t covered yet. Don’t worry though,because we’re going to examine it in great detail:

Page 30: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 27

1 (defn needs-matching-part?

2 [part]

3 (re-find #"^left-" (:name part)))

4

5 (defn make-matching-part

6 [part]

7 {:name (clojure.string/replace (:name part) #"^left-" "right-")

8 :size (:size part)})

9

10 (defn symmetrize-body-parts

11 "Expects a seq of maps which have a :name and :size"

12 [asym-body-parts]

13 (loop [remaining-asym-parts asym-body-parts

14 final-body-parts []]

15 (if (empty? remaining-asym-parts)

16 final-body-parts

17 (let [[part & remaining] remaining-asym-parts

18 final-body-parts (conj final-body-parts part)]

19 (if (needs-matching-part? part)

20 (recur remaining (conj final-body-parts (make-matching-part part)))

21 (recur remaining final-body-parts))))))

22

23 (symmetrize-body-parts asym-hobbit-body-parts)

24 ; => the following is the return value

25 [{:name "head", :size 3}

26 {:name "left-eye", :size 1}

27 {:name "right-eye", :size 1}

28 {:name "left-ear", :size 1}

29 {:name "right-ear", :size 1}

30 {:name "mouth", :size 1}

31 {:name "nose", :size 1}

32 {:name "neck", :size 2}

33 {:name "left-shoulder", :size 3}

34 {:name "right-shoulder", :size 3}

35 {:name "left-upper-arm", :size 3}

36 {:name "right-upper-arm", :size 3}

37 {:name "chest", :size 10}

38 {:name "back", :size 10}

39 {:name "left-forearm", :size 3}

40 {:name "right-forearm", :size 3}

41 {:name "abdomen", :size 6}

42 {:name "left-kidney", :size 1}

Page 31: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 28

43 {:name "right-kidney", :size 1}

44 {:name "left-hand", :size 2}

45 {:name "right-hand", :size 2}

46 {:name "left-knee", :size 2}

47 {:name "right-knee", :size 2}

48 {:name "left-thigh", :size 4}

49 {:name "right-thigh", :size 4}

50 {:name "left-lower-leg", :size 3}

51 {:name "right-lower-leg", :size 3}

52 {:name "left-achilles", :size 1}

53 {:name "right-achilles", :size 1}

54 {:name "left-foot", :size 2}

55 {:name "right-foot", :size 2}]

Let’s break this down!

let

In our symmetrizer above, we saw the following:

1 (let [[part & remaining] remaining-asym-parts

2 final-body-parts (conj final-body-parts part)]

3 some-stuff)

All this does is bind the names on the left to the values on the right. You can think of let as shortfor “let it be”, which is also a beautiful Beatles song (in case you didn’t know (in which case, wtf?)).For example, “Let final-body-parts be (conj final-body-parts part).”

Here are some simpler examples:

1 (let [x 3]

2 x)

3 ; => 3

4

5

6 (def dalmatian-list

7 ["Pongo" "Perdita" "Puppy 1" "Puppy 2"]) ; and 97 more...

8 (let [dalmatians (take 2 dalmatian-list)]

9 dalmatians)

10 ; => ("Pongo" "Perdita")

let also introduces a new scope:

Page 32: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 29

1 (def x 0)

2 (let [x 1] x)

3 ; => 1

However, you can reference existing bindings in your let binding:

1 (def x 0)

2 (let [x (inc x)] x)

3 ; => 1

You can also use rest-params in let, just like you can in functions:

1 (let [[pongo & dalmatians] dalmatian-list]

2 [pongo dalmatians])

3 ; => ["Pongo" ("Perdita" "Puppy 1" "Puppy 2")]

Notice that the value of a let form is the last form in its body which gets evaluated.

let forms follow all the destructuring rules which we introduced in “Calling a Function” above.

One way to think about let forms is that they provide parameters and their arguments side-by-side.let forms have two main uses:

• They provide clarity by allowing you to name things• They allow you to evaluate an expression only once and re-use the result. This is especiallyimportant when you need to re-use the result of an expensive function call, like a networkAPI call. It’s also important when the expression has side effects.

Let’s have another look at the let form in our symmetrizing function so we can understand exactlywhat’s going on:

1 ;; Associate "part" with the first element of "remaining-asym-parts"

2 ;; Associate "remaining" with the rest of the elements in "remaining-asym-parts"

3 ;; Associate "final-body-parts" with the result of (conj final-body-parts part)

4 (let [[part & remaining] remaining-asym-parts

5 final-body-parts (conj final-body-parts part)]

6 (if (needs-matching-part? part)

7 (recur remaining (conj final-body-parts (make-matching-part part)))

8 (recur remaining final-body-parts)))

Notice that part, remaining, and final-body-parts each gets used multiple times in the body ofthe let. If, instead of using the names part, remaining, and final-body-partswe used the originalexpressions, it would be a mess! For example:

Page 33: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 30

1 (if (needs-matching-part? (first remaining-asym-parts))

2 (recur (rest remaining-asym-parts)

3 (conj (conj final-body-parts (first remaining-asym-parts))

4 (make-matching-part (first remaining-asym-parts))))

5 (recur (rest remaining-asm-parts)

6 (conj (conj final-body-parts (first remaining-asym-parts)))))

So, let is a handy way to introduce names for values.

loop

loop provides another way to do recursion in Clojure. Let’s look at a simple example:

1 (loop [iteration 0]

2 (println (str "Iteration " iteration))

3 (if (> iteration 3)

4 (println "Goodbye!")

5 (recur (inc iteration))))

6 ; =>

7 Iteration 0

8 Iteration 1

9 Iteration 2

10 Iteration 3

11 Iteration 4

12 Goodbye!

The first line, loop [iteration 0] begins the loop and introduces a binding with an initial value.This is almost like calling an anonymous function with a default value. On the first pass throughthe loop, iteration has a value of 0.

Next, it prints a super interesting little message.

Then, it checks the value of iteration - if it’s greater than 3 then it’s time to say goodbye. Otherwise,we recur. This is like calling the anonymous function created by loop, but this time we pass it anargument, (inc iteration).

You could in fact accomplish the same thing just using functions:

Page 34: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 31

1 (defn recursive-printer

2 ([]

3 (recursive-printer 0))

4 ([iteration]

5 (println (str "Iteration " iteration))

6 (if (> iteration 3)

7 (println "Goodbye!")

8 (recursive-printer (inc iteration)))))

9 (recursive-printer)

10 ; =>

11 Iteration 0

12 Iteration 1

13 Iteration 2

14 Iteration 3

15 Iteration 4

16 Goodbye!

As you can see, this is a little more verbose. Also, loop has much better performance.

Regular Expressions

Regular expressions are tools for performing pattern matching on text. I won’t go into how theywork, but here’s their literal notation:

1 ;; pound, open quote, close quote

2 #"regular-expression"

In our symmetrizer, re-find returns true or false based on whether the part’s name starts with thestring “left-“:

1 (defn needs-matching-part?

2 [part]

3 (re-find #"^left-" (:name part)))

4 (needs-matching-part? {:name "left-eye"})

5 ; => true

6 (needs-matching-part? {:name "neckbeard"})

7 ; => false

make-matching-part uses a regex to replace "left-" with "right-":

Page 35: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 32

1 (defn make-matching-part

2 [part]

3 {:name (clojure.string/replace (:name part) #"^left-" "right-")

4 :size (:size part)})

5 (make-matching-part {:name "left-eye" :size 1})

6 ; => {:name "right-eye" :size 1}

Symmetrizer

Now let’s analyze the symmetrizer fully. Note points are floating in the ocean, like∼∼∼1∼∼∼:

1 (def asym-hobbit-body-parts [{:name "head" :size 3}

2 {:name "left-eye" :size 1}

3 {:name "left-ear" :size 1}

4 {:name "mouth" :size 1}

5 {:name "nose" :size 1}

6 {:name "neck" :size 2}

7 {:name "left-shoulder" :size 3}

8 {:name "left-upper-arm" :size 3}

9 {:name "chest" :size 10}

10 {:name "back" :size 10}

11 {:name "left-forearm" :size 3}

12 {:name "abdomen" :size 6}

13 {:name "left-kidney" :size 1}

14 {:name "left-hand" :size 2}

15 {:name "left-knee" :size 2}

16 {:name "left-thigh" :size 4}

17 {:name "left-lower-leg" :size 3}

18 {:name "left-achilles" :size 1}

19 {:name "left-foot" :size 2}])

20

21 (defn needs-matching-part?

22 [part]

23 (re-find #"^left-" (:name part)))

24

25 (defn make-matching-part

26 [part]

27 {:name (clojure.string/replace (:name part) #"^left-" "right-")

28 :size (:size part)})

29

30 ; ~~~1~~~

31 (defn symmetrize-body-parts

Page 36: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 33

32 "Expects a seq of maps which have a :name and :size"

33 [asym-body-parts] ;

34 (loop [remaining-asym-parts asym-body-parts ; ~~~2~~~

35 final-body-parts []]

36 (if (empty? remaining-asym-parts) ; ~~~3~~~

37 final-body-parts

38 (let [[part & remaining] remaining-asym-parts ; ~~~4~~~

39 final-body-parts (conj final-body-parts part)]

40 (if (needs-matching-part? part) ; ~~~5~~~

41 (recur remaining (conj final-body-parts (make-matching-part part))) ; \

42 ~~~6~~~

43 (recur remaining final-body-parts))))))

1. This function employs a general strategy which is common in functional programming. Givena sequence (in this case, a vector of body parts and their sizes), continuously split the sequenceinto a “head” and a “tail”. Process the head, add it to some result, and then use recursion tocontinue the process with the tail.

2. Begin looping over the body parts. The “tail” of the sequence will be bound to remaining-

asym-parts. Initially, it’s bound to the full sequence passed to the function, asym-body-parts.Create a result sequence, final-body-parts; its initial value is an empty vector.

3. If remaining-asym-parts is empty, that means we’ve processed the entire sequence and canreturn the result, final-body-parts.

4. Otherwise, split the list into a head, part, and tail, remaining. Also, add part to final-body-

parts and re-bind the result to the name final-body-parts. This might seem weird, and it’sworthwhile to figure out why it works.

5. Our growing sequence of final-body-parts already includes the body part we’re currentlyexamining, part. Here, we decide whether we need to add the matching body part to the list.

6. If so, then add the result of make-matching-part to final-body-parts and recur. Otherwise,just recur.

If you’re new to this kind of programming, this might take some time to puzzle out. Stick with it!Once you understand what’s happening, you’ll feel like a million bucks!

Shorter Symmetrizer with Reduce

The pattern of “process each element in a sequence and build a result” is so common that there’s afunction for it: reduce.

Here’s a simple example:

Page 37: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 34

1 ;; sum with reduce

2 (reduce + [1 2 3 4])

3 ; => 10

This is like telling Clojure to do this:

1 (+ (+ (+ 1 2) 3) 4)

So, reduce works by doing this:

1. Apply the given function to the first two elements of a sequence. That’s where (+ 1 2) comesfrom.

2. Apply the given function to the result and the next element of the sequence. In this case, theresult of step 1 is 3, and the next element of the sequence is 3 as well. So you end up with (+

3 3).3. Repeat step 2 for every remaining element in the sequence.

Reduce also takes an optional initial value. 15 is the initial value here:

1 (reduce + 15 [1 2 3 4])

If you provide an initial value, then reduce starts by applying the given function to the initial valueand the first element of the sequence, rather than the first two elements of the sequence.

To further understand how reduce works, here’s one way that it could be implemented:

1 (defn my-reduce

2 ([f initial coll]

3 (loop [result initial

4 remaining coll]

5 (if (empty? remaining)

6 result

7 (recur (f result (first remaining)) (rest remaining)))))

8 ([f [head & tail]]

9 (my-reduce f head tail)))

We could re-implement symmetrize as follows:

Page 38: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 35

1 (defn better-symmetrize-body-parts

2 "Expects a seq of maps which have a :name and :size"

3 [asym-body-parts]

4 (reduce (fn [final-body-parts part]

5 (let [final-body-parts (conj final-body-parts part)]

6 (if (needs-matching-part? part)

7 (conj final-body-parts (make-matching-part part))

8 final-body-parts)))

9 []

10 asym-body-parts))

Groovy!

Hobbit Violence

My word, this is truly Clojure for the Brave and True!

Now, let’s create a function that will determine which part of the hobbit gets hit:

1 (defn hit

2 [asym-body-parts]

3 (let [sym-parts (better-symmetrize-body-parts asym-body-parts)

4 body-part-size-sum (reduce + 0 (map :size sym-parts))

5 target (inc (rand body-part-size-sum))]

6 (loop [[part & rest] sym-parts

7 accumulated-size (:size part)]

8 (if (> accumulated-size target)

9 part

10 (recur rest (+ accumulated-size (:size part)))))))

11

12 (hit asym-hobbit-body-parts)

13 ; => {:name "right-upper-arm", :size 3}

14

15 (hit asym-hobbit-body-parts)

16 ; => {:name "chest", :size 10}

17

18 (hit asym-hobbit-body-parts)

19 ; => {:name "left-eye", :size 1}

Oh my god, that poor hobbit! You monster!

Page 39: Clojure for the Brave and True - Leanpubsamples.leanpub.com/clojure-for-the-brave-and-true-sample.pdf · ClojurefortheBraveandTrue DanielHigginbotham Thisbookisforsaleat Thisversionwaspublishedon2015-11-02

Do Things: a Clojure Crash Course 36

What Now?

By this point I highly recommend actually writing some code to solidify your Clojure knowledgeif you haven’t started already. The Clojure Cheatsheet² is a great reference listing all the built-infunctions which operate on the data structures we covered.

One great place to start is to Create a function that’s similar to symmetrize-body-parts, except thatit has to work with weird space aliens with radial symmetry. Instead of two eyes, arms, legs, andall that, they have five. Or, do some Project Euler³ challenges. You can also check out 4Clojure⁴, anonline set of Clojure problems designed to test your knowledge. Just write something!

²http://clojure.org/cheatsheet³http://www.projecteuler.net⁴http://www.4clojure.com/problems