Chapter 3. Dipping your toes in the pool

This chapter covers

  • Truthiness
  • Nil punning
  • Destructuring
  • Using the REPL to experiment

Deeper and broader topics will be covered in later chapters, but now’s a good time to pick through an eclectic selection of smaller topics. The topics covered in this chapter stand alone but are important. Covering them now will be a fun way to start digging into practical matters of how to use Clojure.

We covered a lot of conceptual ground in the previous chapter and built your Clojure lexicon. In this chapter, we’ll take a detour into some fundamental underpinnings relevant to every Clojure project. First we’ll explore Clojure’s straightforward notions of truthiness,[1] or the distinctions between values considered logical true and those considered logical false. We’ll discuss Clojure’s extremely simple rules followed by the notion of nil punning, or treating an empty sequence as nil. Those of you coming from a background in Lisp may recognize the term, but Clojure handles nil punning differently. We’ll discuss the idioms related to nil punning in Clojure and their rationale. We’ll then cover destructuring—a powerful mechanism for pulling apart collection types and binding their constituent parts as individual values. Using destructuring in your own code can often lead to extremely concise and elegant solutions, and we’ll provide some examples to illustrate this. Finally, we’ll sit down and pair-program together to help you gain an appreciation for the power of Clojure’s Read-Eval-Print Loop (REPL).

1 As a deviation from the definition coined by Stephen Colbert in his television show The Colbert Report. Ours isn’t about matters of gut feeling but rather about matters of Clojure’s logical truth ideal.

3.1. Truthiness

Truthfulness may be an important virtue, but it doesn’t come up much in programming. On the other hand, truthiness, or the matter of logical truth values in Clojure, is critical.

Clojure has one Boolean context: the test expression of the if form. Other forms that expect Booleans—and, or, when, and so forth—are macros built on top of if. It’s here that truthiness matters.

3.1.1. What’s truth?

Every value looks like true to if, except for false and nil. That means values some languages treat as false—zero-length strings, empty lists, the number zero, and so on—are all treated as true in Clojure:

(if true :truthy :falsey)  ;=> :truthy
(if [] :truthy :falsey)    ;=> :truthy
(if nil :truthy :falsey)   ;=> :falsey
(if false :truthy :falsey) ;=> :falsey

This may feel uncomfortable to you, depending on your background. But because branches in a program’s logic are already one of the most likely places for complexity and bugs, Clojure has opted for a simple rule. There’s no need to check a class’s definition to see if it acts like “false” when you think it should (as is sometimes required in Python, for example). Every object is “true” all the time, unless it’s nil or false.

3.1.2. Don’t create Boolean objects

It’s possible to create an object that looks a lot like, but isn’t actually, false. Java has left a land mine for you here, so take a moment to look at it so that you can step past it gingerly and get on with your life:

(def evil-false (Boolean. "false")) ; NEVER do this

This creates a new instance of Boolean—and that’s already wrong! Because there are only two possible values of Boolean, an instance of each has already been made for us—they’re named true and false.[2] But here we’ve gone and done it anyway, created a new instance of Boolean and stored it in a var named evil-false. It looks like false:

2 Clojure’s true and false instances are the same as Java’s Boolean/TRUE and Boolean/FALSE, respectively.

evil-false
;=> false

Sometimes it even acts like false:

(= false evil-false)
;=> true

But once it gains your trust, it’ll show you just how wicked it is by acting like true:

(if evil-false :truthy :falsey)
;=> :truthy

Java’s own documentation warns against the creation of this evil thing, and now you’ve been warned again. If you want to parse a string, use the Boolean class’s static valueOf method instead of its constructor. This is the right way:

(if (Boolean/valueOf "false") :truthy :falsey)
;=> :falsey

3.1.3. nil vs. false

Rarely do you need to differentiate between the two non-truthy values, but if you do, you can use nil? and false?:

(when (nil? nil) "Actually nil, not false")
;=> "Actually nil, not false"

Keeping in mind the basic rule that everything in Clojure is truthy unless it’s false or nil is an astonishingly powerful concept, allowing for elegant solutions. Often programming languages have complicated semantics for truthiness, but Clojure manages to avoid those matters nicely.

Building on that theme, we’ll now discuss the matter of nil punning, which may or may not surprise you, depending on your background.

3.2. Nil pun with care

Because empty collections act like true in Boolean contexts, you need an idiom for testing whether there’s anything in a collection to process. Thankfully, Clojure provides such a technique:

(seq [1 2 3])
;=> (1 2 3)

(seq [])
;=> nil

The seq function returns a sequence view of a collection, or nil if the collection is empty. In a language like Common Lisp, an empty list acts as a false value and can be used as a pun (a term with the same behavior) for such in determining a looping termination. As you saw in section 3.1, Clojure’s empty sequences are instead truthy, and therefore to use one as a pun for falsity will lead to heartache and despair. One solution that might come to mind is to use empty? in the test, leading to the awkward phrase (when-not (empty? s) ...). A better solution is to use seq as a termination condition, as in the following function print-seq:

There are a number of points to take away from this example. First, the use of seq as a terminating condition is the preferred way to test whether a sequence is empty . If you tested just s instead of (seq s), then the terminating condition wouldn’t occur even for empty collections, leading to an infinite loop. Thankfully, the use of seq allows you to properly check for an empty collection:

(print-seq [])
;=> nil

Second, rest is used instead of next to consume the sequence on the recursive call. Although they’re nearly identical in behavior, rest can return a sequence that’s either empty or not empty (has elements) , but it never returns nil. On the other hand, next returns a seq of the rest, or (seq (rest s)), and thus never returns an empty sequence, returning nil in its place. It’s appropriate to use rest here because you’re using seq explicitly in each subsequent iteration. Observe:

(print-seq [1 2])
; 1
; 2
;=> nil

As shown, the print-seq function uses seq and recursion (via recur) to “consume” a collection, printing its elements along the way using prn. In fact, print-seq is a template for most functions in Clojure: it shows that you generally shouldn’t assume seq has been called on your collection arguments, but instead call seq in the function itself and process based on its result. Using this approach fosters a more generic handling of collections, a topic that we explore in great detail in chapter 5. In the meantime, it’s important to keep in mind the difference between empty collections and false values; otherwise your attempts at nil punning may cause groans all around.

Note

An important point to mention is that it would be best to use doseq to iterate over the collection rather than an explicit recursion (see section 1.1.5), but that wouldn’t allow us to illustrate the point at hand: the Clojure forms named with do at the start (doseq, dotimes, do, and so on) are intended for side effects in their bodies and generally return nil as their results.

To top off our trifecta of core Clojure concepts, we next explore the most powerful of the three: destructuring. You’ll see how powerful this mini-language in Clojure can be toward developing elegant and often beautiful solutions.

3.3. Destructuring

In the previous section, we briefly described Clojure’s destructuring facility as a mini-language embedded in Clojure. Destructuring allows you to positionally bind locals based on an expected form for a composite data structure. In this section, we’ll explore how destructuring can be used to pull apart composite structures into bindings through the lens of a simple rolodex example project.

Note

Destructuring is loosely related to pattern matching found in Haskell, KRC, or Scala, but it only provides place binding and not conditional binding. For full-featured pattern matching in Clojure, consider using http://github.com/clojure/core.match, which might become a core feature one day.

3.3.1. Your assignment, should you choose to accept it

You’ve heard that the rolodex project has been overdue, but now every developer assigned to it is out sick. The QA team is ready to go, but one function is still missing, and it’s a show-stopper. You’re told to drop everything and write the function ASAP.

The design? Take a vector of length 3 that represents a person’s first, middle, and last names, and return a string that will sort in the normal way, like “Steele, Guy Lewis”. What are you waiting for? Why aren’t you done yet?!?!

(def guys-whole-name ["Guy" "Lewis" "Steele"])

(str (nth guys-whole-name 2) ", "
     (nth guys-whole-name 0) " "
     (nth guys-whole-name 1))
;=> "Steele, Guy Lewis"

Alas, by the time you’ve finished typing guys-whole-name for the fourth time, it’s too late. The customers have cancelled their orders, and the whole department has been downsized.

If only you’d known about destructuring.

OK, so you’re not likely to lose your job because your function is twice as many lines as it needs to be, but still, that’s a lot of code repeated in a pretty small function. And using index numbers instead of named locals makes the purpose of the function more obscure than necessary.

Destructuring solves both these problems by allowing you to place a collection of names in a binding form where normally you’d put a single name. One kind of binding form is the list of parameters given in a function definition.

3.3.2. Destructuring with a vector

Let’s try that again but use destructuring with let to create more convenient locals for the parts of Guy’s name:

(let [[f-name m-name l-name] guys-whole-name]
  (str l-name ", " f-name " " m-name))
Positional destructuring

This positional destructuring doesn’t work on maps and sets because they’re not logically[3] aligned sequentially. Interestingly, positional destructuring works with Java’s java.util.regex.Matcher:

3 Technically, positional destructuring might make sense with sorted sets and maps, but alas it doesn’t operate as such because Clojure doesn’t consider them to be sequential collections. See section 5.2.

(def date-regex #"(d{1,2})/(d{1,2})/(d{4})")

(let [rem (re-matcher date-regex "12/02/1975")]
  (when (.find rem)
     (let [[_ m d] rem]
       {:month m :day d})))

;;=> {:month "12", :day "02"}

Although it’s possible to destructure a Matcher object, a better solution would be to destructure the result of (re-find rem) instead. Finally, positional destructuring also works for anything implementing the CharSequence and java.util.Random-Access interfaces.

This is the simplest form of destructuring, where you want to pick apart a sequential thing (a vector of strings in this case, although a list or other sequential collection would work as well), giving each item a name.

You don’t need it here, but you can also use an ampersand in a destructuring vector to indicate that any remaining values of the input should be collected into a (possibly lazy) seq:

(let [[a b c & more] (range 10)]
  (println "a b c are:" a b c)
  (println "more is:" more))
; a b c are: 0 1 2
; more is: (3 4 5 6 7 8 9)
;=> nil

Here the locals a, b, and c are created and bound to the first three values of the range. Because the next symbol is an ampersand, the remaining values are made available as a seq bound to more.

The final feature of vector destructuring is :as, which can be used to bind a local to the entire collection. It must be placed after the & local, if there is one, at the end of the destructuring vector:

(let [range-vec (vec (range 10))
      [a b c & more :as all] range-vec]
  (println "a b c are:" a b c)
  (println "more is:" more)
  (println "all is:" all))
; a b c are: 0 1 2
; more is: (3 4 5 6 7 8 9)
; all is: [0 1 2 3 4 5 6 7 8 9]
;=> nil

range-vec is a vector in this example, and the directive :as binds the input collection as is, entirely unmolested, so that the vector stays a vector. This is in contrast to &, which binds more to a seq, not a vector.

3.3.3. Destructuring with a map

Perhaps passing a name as a three-part vector wasn’t a good idea in the first place. It might be better stored in a map:

(def guys-name-map
  {:f-name "Guy" :m-name "Lewis" :l-name "Steele"})

But now you can’t use a vector to pick it apart. Instead, you use a map:

(let [{f-name :f-name, m-name :m-name, l-name :l-name} guys-name-map]
  (str l-name ", " f-name " " m-name))

;=> "Steele, Guy Lewis"

A couple things about this example may jump out at you. One might be that it still seems repetitive—we’ll get to that in a moment.

Another might be that the way the keywords are organized looks unusual. The example has its keywords like :f-name on the right side of each pair, even though the input map had keywords on the left. There are a couple reasons for that. The first is to help keep the pattern of the name on the left getting the value specified by the thing on the right. So the new local f-name gets the value looked up in the map by the key :f-name, just as the whole map gets its value from guys-name-map in the earlier def form.

The second reason is because it allows you to conjure up other destructuring features by using forms that would otherwise make no sense. Because the item on the left of each pair will be a new local name, it must be a symbol or possibly a nested destructuring form. But one thing it can’t be is a keyword, unless the keyword is a specially supported feature such as :keys, :strs, :syms, :as, and :or.

We’ll discuss the :keys feature first because it nicely handles the repetitiveness we mentioned earlier. It allows you to rewrite your solution like this:

(let [{:keys [f-name m-name l-name]} guys-name-map]
  (str l-name ", " f-name " " m-name))

;=> "Steele, Guy Lewis"

So by using :keys instead of a binding form, you’re telling Clojure that the next form will be a vector of names that it should convert to keywords such as :f-name in order to look up their values in the input map. Similarly, if you’d used :strs, Clojure would be looking for items in the map with string keys such as "f-name", and :syms would indicate symbol keys.

The directives :keys, :strs, :syms, and regular named bindings can appear in any combination and in any order. But sometimes you’ll want to get at the original map—in other words, the keys you didn’t name individually by any of the methods just described. For that, you want :as, which works just like it does with vector destructuring:

(let [{f-name :f-name, :as whole-name} guys-name-map]
  (println "First name is" f-name)
  (println "Whole name is below:")
  whole-name)
;; First name is Guy
;; Whole name is below:
;;=> {:f-name "Guy", :m-name "Lewis", :l-name "Steele"}

If the destructuring map looks up a key that’s not in the source map, it’s normally bound to nil, but you can provide different defaults with :or:

(let [{:keys [title f-name m-name l-name],
       :or {title "Mr."}} guys-name-map]
  (println title f-name m-name l-name))

; Mr. Guy Lewis Steele
;=> nil

All of these map destructuring features also work on lists, a feature that’s primarily used by functions so as to accept keyword arguments:

(defn whole-name [& args]
  (let [{:keys [f-name m-name l-name]} args]
    (str l-name ", " f-name " " m-name)))

(whole-name :f-name "Guy" :m-name "Lewis" :l-name "Steele")
;=> "Steele, Guy Lewis"

Note that when defined this way, whole-name isn’t called with a map parameter, but rather with arguments that alternate between keys and values. Using a map to destructure this list of arguments causes the list to first be poured into a map collection before then being destructured as usual.

Associative destructuring

One final technique worth mentioning is associative destructuring. Using a map to define a number of destructure bindings isn’t limited to maps. You can also destructure a vector by providing a map declaring the local name as indices into them, as shown:

(let [{first-thing 0, last-thing 3} [1 2 3 4]]
  [first-thing last-thing])
;=> [1 4]

You’ve seen the shapes that destructuring takes in the let form, but you’re not limited to that exclusively, as we’ll explore next.

3.3.4. Destructuring in function parameters

All the preceding examples use let to do their destructuring, but exactly the same features are available in function parameters. Each function parameter can destructure a map or sequence:

(defn print-last-name [{:keys [l-name]}]
  (println l-name))

(print-last-name guys-name-map)
; Steele

Note that function arguments can include an ampersand as well, but this isn’t the same as destructuring. Instead, that’s part of their general support for multiple function bodies, each with its own number of parameters.

3.3.5. Destructuring vs. accessor methods

In many object-oriented languages, you might create new classes to manage your application data objects, each with its own set of getter and setter methods. Clojure instead is built to facilitate the use of maps and vectors to create abstractions. This makes destructuring natural and straightforward. Any time you find that you’re calling nth on the same collection a few times, or looking up constants in a map, or using first or next, consider using destructuring instead.

Now that we’ve made it through the cursory introduction to Clojure, let’s take some time to pair-program (Williams 2002). In the next section, we’ll take many of the bare necessities that you’ve just learned and walk through the creation of a couple interesting functions for drawing pretty pictures in Clojure’s REPL.

3.4. Using the REPL to experiment

Most software development projects include a stage where you’re not sure what needs to happen next. Perhaps you need to use a library or part of a library you’ve never touched before. Or perhaps you know what your input to a particular function will be, and what the output should be, but you aren’t sure how to get from one to the other. In some programming languages, this can be time-consuming and frustrating; but by using the power of the Clojure REPL, the interactive command prompt, it can be fun.

3.4.1. Experimenting with seqs

Say someone suggests to you that coloring every pixel of a canvas with the xor of its x and y coordinates might produce an interesting image. It shouldn’t be too hard, so you can jump right in. You’ll need to perform an operation on every x and y in a pair of ranges. Do you know how range works?

(range 5)
;=> (0 1 2 3 4)

That should do nicely for one coordinate. To nest seqs, for often does the trick. But again, rather than writing code and waiting until you have enough to warrant compiling and testing, you can try it:

(for [x (range 2) y (range 2)] [x y])
;=> ([0 0] [0 1] [1 0] [1 1])

There are the coordinates that will form your input. Now you need to xor them:

(xor 1 2)
;=> java.lang.Exception: Unable to resolve symbol: xor in this context

Bother—no function named xor. Fortunately, Clojure provides find-doc, which searches not just function names but also their doc strings for the given term:[4]

4 Two community-run sources of Clojure information worth bookmarking are http://clojuredocs.org, a site providing API documentation and examples, and http://clojure-doc.org, a site providing tutorials, guides, and cookbooks.

(find-doc "xor")
; -------------------------
; clojure.core/bit-xor
; ([x y]) ([x y & more])
;   Bitwise exclusive or
;=> nil

So the function you need is called bit-xor:

(bit-xor 1 2)
;=> 3

Perfect! Next you want to adjust the earlier for form to return bit-xor along with x and y. The easiest way to do this depends on what tool is hosting your REPL. In many, you can press the up-arrow key on your keyboard a couple of times to bring back the earlier for form. You won’t want to retype things to make minor adjustments, so take a moment right now to figure out a method you like that will let you make a tweak like this by inserting the bit-xor call:

(for [x (range 2) y (range 2)]
  [x y (bit-xor x y)])

;=> ([0 0 0] [0 1 1] [1 0 1] [1 1 0])

That looks about right. We’re about to shift gears to pursue the graphics side of this problem, so tuck away that bit of code in a function so it’ll be easy to use later:

(defn xors [max-x max-y]
  (for [x (range max-x) y (range max-y)]
       [x y (bit-xor x y)]))

(xors 2 2)
;=> ([0 0 0] [0 1 1] [1 0 1] [1 1 0])

You might even save that into a .clj file, if you haven’t already. It’s worth mentioning that saving files with the .clj extension is standard for Clojure source code, whereas .cljs is standard for ClojureScript files.

3.4.2. Experimenting with graphics

Clojure’s REPL isn’t just for playing around; it’s also great for experimenting with Java libraries. We believe there’s no better environment for exploring a Java API than Clojure’s REPL. To illustrate, poke around with java.awt, starting with a Frame:

(def frame (java.awt.Frame.))
;=> #'user/frame

That should have created a Frame, but no window appeared. Did it work at all?

frame
;=> #<Frame java.awt.Frame[frame0,0,22,0x0,invalid,hidden,...]>

Well, you have a Frame object, but perhaps the reason you can’t see it is hinted at by the word hidden in the #<Frame...> printout. Perhaps the Frame has a method you need to call to make it visible. One way to find out would be to check the Javadoc of the object, but because you’re at the REPL already, let’s try something else. You’ve already seen how the for macro works, so maybe you can check a class for which methods it has to see whether one you can use is available:

The for macro provides a way to iterate over a collection, performing some action on each item and collecting the results into a sequence. The preceding example iterates over a sequence of the methods available on the java.awt.Frame class . Whenever Clojure encounters a symbol that looks like a Java class name, it attempts to resolve it as a class. This behavior allows you to then call the getMethods method directly on the class. Next, a :let flag and bindings vector is used , working similarly to the let special form that you use to bind the local method-name to the result of calling the method .getName on each method in turn. The :when is used to limit the elements used in its body to only those that return a truthy value in the expression after the directive . Using these directives lets you iterate through the methods and build a seq of those whose names match a regular expression #"Vis". We’ll cover Clojure’s regular-expression syntax in section 3.5.

Your query returns two potential methods, so try each of them:

(.isVisible frame)
;=> false

That’s false, as you might have suspected. Will setting it to true make any difference?

(.setVisible frame true)
;=> nil

It did, but it’s so tiny! Not to worry, because a Frame class also has a .setSize method you can use:

(.setSize frame (java.awt.Dimension. 200 200))
;=> nil

And up pops a blank window for you to draw on. At this point, we’ll guide you through the rest of this section; but keep in mind that Java’s official API might be of interest, should you choose to extend the example program.

Note

The Clojure clojure.java.javadoc namespace has a javadoc function to query and view official API documentation: (javadoc frame). This should return a string corresponding to a URL and open a browser window for the right page of documentation. Prior to Clojure 1.2, this function was in clojure.contrib.repl-utils.

What you need to draw into your Frame is its graphics context, which can be fetched as shown:

(def gfx (.getGraphics frame))
;=> #'user/gfx

Then, to draw, you can try out the fillRect method of that graphics context. If you’re trying this yourself, make sure the blank window is positioned so that it’s unobscured while you’re typing into your REPL:

(.fillRect gfx 100 100 50 75)

And just like that, you’re drawing on the screen interactively. You should see a single black rectangle in the formerly empty window. Exciting, isn’t it? You could be a kid playing with turtle graphics for the first time, it’s so much fun. But what it needs is a dash of color:

(.setColor gfx (java.awt.Color. 255 128 0))
(.fillRect gfx 100 150 75 50)

Now there should be an orange rectangle as well. Perhaps the coloring would make Martha Stewart cry, but you’ve tried all the basic building blocks you’ll need to complete the original task: you have a function that returns a seq of coordinates and their xor values, you have a window you can draw into, and you know how to draw rectangles of different colors. Bear in mind that if you move the frame with the mouse, your beautiful graphics may disappear (depending on your OS and window manager). This is an artifact of this limited experiment and can be avoided using the full Java Swing capabilities.[5]

5 A nice open source Swing library for Clojure is Seesaw, available at http://github.com/daveray/seesaw.

3.4.3. Putting it all together

What’s left to do? Use the graphics functions you just saw to draw the xor values:

(doseq [[x y xor] (xors 200 200)]
  (.setColor gfx (java.awt.Color. xor xor xor))
  (.fillRect gfx x y 1 1))

The xors function you created earlier generates a seq of vectors, if you remember, where each vector has three elements: the x and y for your coordinates and the xor value that goes with them. The first line here uses destructuring to assign each of those three values to new locals x, y, and xor, respectively.

The second line sets the “pen” color to a gray level based on the xor value, and the final line draws a single-pixel rectangle at the current coordinates. The resulting graphic is shown in figure 3.1.

Figure 3.1. Visualization of xor. This is the graphic drawn by the 10 or so lines of code we’ve looked at so far—a visual representation of Clojure’s bit-xor function.

But just because you’ve succeeded doesn’t mean you have to quit. You’ve built up some knowledge and a bit of a toolbox, so why not play with it a little?

3.4.4. When things go wrong

For example, the pattern appears to cut off in the middle—perhaps you’d like to see more. Re-enter that last expression, but this time try larger limits:

(doseq [[x y xor] (xors 500 500)]
  (.setColor gfx (java.awt.Color. xor xor xor))
  (.fillRect gfx x y 1 1))
; java.lang.IllegalArgumentException:
;    Color parameter outside of expected range: Red Green Blue

Whoops. Something went wrong, but what exactly? This gives you a perfect opportunity to try one final REPL tool. When an exception is thrown from something you try at the REPL, the result is stored in a var named *e. This allows you to get more detail about the expression, such as the stack trace:

(.printStackTrace *e)
; java.lang.IllegalArgumentException: Color parameter outside of
;         expected range: Red Green Blue
; at clojure.lang.Compiler.eval(Compiler.java:4639)
; at clojure.core$eval__5182.invoke(core.clj:1966)
; at clojure.main$repl__7283$read_eval_print__7295.invoke(main.clj:180)
; ...skipping a bit here...
; Caused by: java.lang.IllegalArgumentException: Color parameter

;         outside of expected range: Red Green Blue
; at java.awt.Color.testColorValueRange(Color.java:298)
; at java.awt.Color.<init>(Color.java:382)
; ...skipping a bit more...
; ... 11 more
;=> nil

That’s a lot of text, but don’t panic. Learning to read Java stack traces is useful, so let’s pick it apart.

The first thing to understand is the overall structure of the trace—there are two “causes.” The original or root cause of the exception is listed last—this is the best place to look first.[6] The name and text of the exception there are the same as the REPL printed for you in the first place, although they won’t always be. So let’s look at that next line:

6 This is a runtime exception, the most common kind. If you misuse a macro or find a bug in one, you may see compile-time exceptions. The trace will look similar but will have many more references to Compiler.java. For these traces, the most recent exception (listed first) may be the only one that identifies the filename and line number in your code that’s at fault.

at java.awt.Color.testColorValueRange(Color.java:298)

Like most lines in the stack trace, this has four parts—the name of the class, the name of the method, the filename, and finally the line number:

at <class>.<method or constructor>(<filename>:<line>)

In this case, the function name is testColorValueRange, which is defined in Java’s own Color.java file. Unless this means more to you than it does to us, let’s move on to the next line:

at java.awt.Color.<init>(Color.java:382)

It appears that it was the Color’s constructor (called <init> in stack traces) that called the test function you saw earlier. So now the picture is pretty clear—when you constructed a Color instance, it checked the values you passed in, decided they were invalid, and threw an appropriate exception.

If this isn’t enough, you can continue walking down the stack trace until the line

... 11 more

This is your cue to jump up to the cause listed before this one to find out what the next 11 stack frames were.

To fix your invalid Color argument, you can adjust the xors function to return only legal values using the rem function, which returns the remainder so you can keep the results under 256:

(defn xors [xs ys]
  (for [x (range xs) y (range ys)]
    [x y (rem (bit-xor x y) 256)]))

Note that you’re redefining an existing function here. This is perfectly acceptable and well-supported behavior. Before moving on, create a function that takes a graphics object and clears it:

(defn clear [g] (.clearRect g 0 0 200 200))

Calling (clear gfx) clears the frame, allowing the doseq form you tried before to work perfectly.

3.4.5. Just for fun

The bit-xor function does produce an interesting image, but it would be fun to explore what different functions look like. Try adding another parameter to xors so that you can pass in whatever function you’d like to look at. Because it’s not just bit-xor anymore, change the name while you’re at it:

(defn f-values [f xs ys]
  (for [x (range xs) y (range ys)]
    [x y (rem (f x y) 256)]))

You might as well wrap your call to setSize, clear, and the doseq form in a function as well:

(defn draw-values [f xs ys]
  (clear gfx)
  (.setSize frame (java.awt.Dimension. xs ys))
  (doseq [[x y v] (f-values f xs ys)]
    (.setColor gfx (java.awt.Color. v v v))
    (.fillRect gfx x y 1 1)))

This allows you to try different functions and ranges easily. More nice examples are shown in figure 3.2, resulting from the following:

(draw-values bit-and 256 256)
(draw-values + 256 256)
(draw-values * 256 256)
Figure 3.2. The draw-values function you’ve written can be used to create a variety of graphics. Here are examples, from left to right, of bit-and, +, and *.

If this were the beginning or some awkward middle stage of a large project, you’d have succeeded in pushing past this troubling point and could now take the functions you’ve built and drop them into the larger project.

By trying everything out at the REPL, you’re encouraged to try smaller pieces rather than larger ones. The smaller the piece, the shorter the distance down an incorrect path you’re likely to go. Not only does this reduce the overall development time, but it provides developers more frequent successes that can help keep morale and motivation high through even tough stages of a project. But trial-and-error exploration isn’t enough. An intuitive basis in Clojure is also needed to become highly effective. Throughout this book, we’ll help you to build your intuition in Clojure through discussions of its idioms and its motivating factors and rationale.

3.5. Summary

We started slowly in this chapter in order to take a breather from the sprint that was chapter 2. Truthiness in Clojure observes a simple rule: every object is true all the time, unless it’s nil or false. Second, in many Lisp-like languages, the empty list () and the truth value nil are analogous—this is known as nil punning—but in Clojure this isn’t the case. Instead, Clojure employs the (seq (rest _)) idiom in the form of the next function to provide a mechanism fostering “form follows function” and also to eliminate errors associated with falsity/empty-seq disparity. Finally, destructuring provides a powerful mechanism, a mini-language for binding if you will, for partially or entirely pulling apart the constituent components of composite types. Our trek through the REPL illustrated the power in having the whole language (Graham 2001) at your disposal. As a Clojure programmer, you’ll spend a lot of time in the REPL, and pretty soon you won’t know how you lived without it.

In the next chapter, we’ll touch on matters concerning Clojure’s seemingly innocent scalar data types. Although in most cases these scalars will expose powerful programming techniques, be forewarned: as you’ll see, the picture isn’t always rosy.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.19.14.243