By Developers, For Developers

Historical errata for Programming Clojure

PDF PgPaper PgTypeDescriptionFixed onComments
15SUGGEST

This page demonstrates Clojure’s elegance by comparing a Java isBlank function against a Clojure blank? function. The isBlank function checks against any type of whitespace, so you might want to put more than just \\space in the test set. Maybe throw a \\tab in there for clarity. If you wanted to be overly complete I guess you could define a set with all the unicode whitespace characters.

Also, the use of a multimethod there seems odd. I guess it covers the nil case (why is there a nil case?) and allows adding more classes later, but maybe this would be more representative of elegance?

(defn blank? [s] (every? #{\\space \\tab} s) )

Now it handles any sequence of characters, and still works on nil. It does make the bullet point below it about multimethods need a new home though.

2008-11-14
85TYPO

instead of after (vector 1 2 3), causing normal paragraphs to be TRUNC’d.

2008-11-14
31TYPO

The last sentence in the first paragraph reads “Clojure then takes executes the forms.”

2008-11-14
9TYPO

Grammer improvement page 9 paragraph 3:
“This book will appeal to [the] three communities that Clojure is introducing to the mainstream”

2008-11-14
41TYPO

4th paragraph, first sentence reads “A third reason to use anonymous functions is when you dynamically
creating a function at runtime.” Missing an “are” between “you” and “dynamically”, or change “creating” to “create”?

2008-11-14
27TYPO

I think the Fibonacci example syntax should be:
(take 10 introduction/fibs)
using Clojure svn rev 1086

2008-11-14
28TYPO

Last paragraph before “Wrapping Up” says

“Of you are having trouble…”
it should say
“If you are having trouble…”

2008-11-14
46TYPO

Fourth paragraph (minus code samples) ends with this sentence:

“For this chapter, you can load reload the examples at any time:”

Should “load” be removed from the sentence?

2008-11-14
82TYPO

If you see the phrase “foo changes xml,” mentally substitute “foo returns a changed copy of x.”

should be “… changed copy of xml” or “foo changes x”

2008-11-14
44TYPO

The regex in ellipsize definition is #“\\\\s” and it should be #“\\s”.

Copy pasting the code example, using clojure at v1086, doesn’t work

2008-11-14
37ERROR

(symbol? :hello) should probably be (symbol? ’hello)

2008-11-14
49TYPO

paragraph 3. … But you many not use it very often, as many common recursions are provided by Clojure’s sequence library…
Change make to may?

2008-11-14
61TYPO

paragraph 4 … In Java, arrays are have their own syntax,
and their own bytecode instructions …
Extra ‘are’.

2008-11-14
53SUGGEST

Two things confused me a bit here, when I tried to define the index-of-any function in my REPL:

1) The “indexed” function is in clojure.contrib.seq-utils, not in core Clojure, so I got an exception because I hadn’t mapped that namespace.

2) The argument for chars needs to be a set. I tried to call it with a vector first, which didn’t work, of course. Things became even more confusing when I looked at the documentation for “get” in the Clojure API docs, and it only talked about maps.

Maybe these things could be clarified in the text?

2008-11-14
35TYPO

“Only nil? is nil, and only false is false?.” presumably should be “Only nil is nil?, …”

2008-11-14
39TYPO

My guess is that the regular expression passed to re-split in the example using indexable-word? has an extra backslash. (This is also true where fn is used instead of the defn of indexable-word? on the next page.) In short, #“\\\\W+” should be replaced in at least two places with #“\\W+”

2008-11-14
49SUGGEST

recur binds new values for loop’s exprs, and returns control to the top of the loop.

Shouldn’t this read, “… for loop’s bindings, …”? The “exprs” in the loop form are the body of the loop; it is the bindings that are replaced, not the exprs.

2008-11-14
39TYPO

Newest clojure now works with \\W instead of \\\\W

2008-11-14
43TYPO

last line of code in the page
(greet-author-1 {:last-name “Vinge” :first-name “Vernor”})

should be
(greet-author-2 {:last-name “Vinge” :first-name “Vernor”})

2008-11-14
18TYPO

(defn hello-world [name]
(println (format “Hello, %s”) name)

should be

(defn hello-world [name]
(println (format “Hello, %s” name))

2008-11-14
25TYPO

Lines 4 through 6 return different strings based on the the user was a visitor in the past.

the the
I’m guessing should be
if the

2008-11-14
65TYPO

With duck typing, an object’s type is the sum of what it can
do (methods), rather than the sum of what it is (the inheritance hierarchy). In other words, it is more import to quack( ) and fly( ) than it is to insist that you implement the Duck interface.

it should say:

… In other words, it is more important to quack( ) and fly( )…

2008-11-14
8TYPO

Clojure provides alternatives
to lock-based concurrency: software transactional memory,
agent[s], and dynamic variables.

2008-11-14
47TYPO

Last sentence of 5th paragraph: “If it did, it would always evaluate
both the “in” and “else” forms, regardless of input." Did you mean “if” and “else”?

2008-11-14
89TYPO

seems to be an extra “>” on the example after the %s

(for [word [“the” “quick” “brown” “fox”]]
(format "

%s>

" word))
-> ("

the>

" "

quick>

" "

brown>

" "

fox>

")

2008-11-14
26TYPO

2nd paragraph after 1.4 Using the Sample Code

One you have downloaded the samples, …

Once you have downloaded the samples,

2008-11-14
2323ERROR

I’m using clojure_20080916.zip and the *1, *2, *3, and *e don’t work in the REPL. I get clojure.lang.Compiler$CompilerException: NO_SOURCE_FILE:5: Unable to resolve symbol: *1 in this context

2008-11-14
15TYPO

missing “return true” at the end of the isBlank function in Java. I verified the /org/apache/commons/lang/StringUtils.java file.

public static boolean isBlank(String str) {
int strLen;
if (str null || (strLen = str.length()) 0) {
return true;
}
for (int i = 0; i < strLen; i) {
if ((Character.isWhitespace(str.charAt(i)) == false)) {
return false;
}
}
return true;
}

2008-11-14
2323ERROR

Ahh the svn version works fine (*1, *2, *3, *e)

2008-11-14
92SUGGEST

“; you probably don’t want to cons, see below”
this line should pribibly go below:

(cons 72 (.getBytes “ello”))
-> (72 101 108 108 111)

2008-11-14
101TYPO

(def inventors {“java” “gosling” , “clojure” “hickey” , “ruby” , “matz” })

the last \\, could make the statement a little less clear than if it where omitted

2008-11-14
15TYPO

isBlank Java code is missing a closing brace (as well as return true). Indentation seems off too.

2008-11-14
66ERROR

In the type-hinted version of the sum-to function, it is defined as integer-sum-to, but the benchmark form uses faster-sum-to:

(dotimes _ 5 (time (faster-sum-to 10000))

2008-11-14
57TYPO

“Powerful, in that it bring all the expressiveness…”, should be “brings”

2008-11-14
60SUGGEST

The use of the Classname/membername form without parentheses only works with static fields, not static methods. Should it be clarified here?

user=> Math/PI
Math/PI
3.141592653589793
user=> System/currentTimeMillis
System/currentTimeMillis
java.lang.NoSuchFieldException: currentTimeMillis (NO_SOURCE_FILE:0)
user=> (System/currentTimeMillis)
(System/currentTimeMillis)
1226043562161

2008-11-14
61SUGGEST

In the first paragraph about idiomatic Clojure: I would also like to hear your take on (. class-or-instance member) vs. (.member class-or-instance). When to prefer one over the other? Should one form be considered more idiomatic?

2008-11-14
61TYPO

Paragraph 2: “So, you should be able to use Clojure’s collections from with Clojure…” should be “from within”?

2008-11-14
69TYPO

Second paragraph from the bottom is missing a close paren:

“To get ready for this example, go ahead and import the following classes (we’ll need them all before we are done:”

2008-11-14
69TYPO

In the last paragraph: “The easiest was is often to extend the DefaultHandler class.”

I’m guessing this should be “The easiest way”.

2008-11-14
70TYPO

In the description of the DefaultHandler proxy:
“The proxy above has one method. Its name is startElement, and it takes four arguments and prints the name of the name arg.”

It actually prints the name of the qname arg, not name.

Also, in the JavaDoc for DefaultHandler.startElement, the name of the first argument is actually “uri”, so maybe the attribute vector of the proxy should be [uri, local, qname, atts].

2008-11-14
71ERROR

Two problems in the following code example:

(dotimes i 5
(.start
(Thread.
(#(fn []
(Thread/sleep (rand 1000))
(println “Finished %d on %s” i (Thread/currentThread)))))))

1) println does not format it’s arguments, so the output looks like “Finished %d on %s 3 #<Thread[Thread-30,5,main]>”

2) The nested function definitions look a bit strange to me. Why is the function wrapped in (#(…))? Wouldn’t a simple fn do the trick?

2008-11-14
80TYPO

In the code examples that follow the introduction of cloj and into:

(conj sequence element & elements)
(conj to-sequence from-sequence)

The latter should probably be an into, not a conj?

2008-11-14
37TYPO

In 2.3 Functions,

(symbol? :hello)
true

should be changed to

(symbol? ’hello)
true

2008-11-14
27TYPO

(take 10 introduction.fibs) does not work.. Is ‘introduction/fibs’ right, isn’t it?

2008-11-14
69TYPO

In the PDF version, with syntax coloring, the “and” in “class-and-interfaces” gets colored as a function name; same in “gen-and-save-class” a few pages later.

2009-05-27
32SUGGEST

When referring to Clojure functions I would not use the format f(), as that’s not how functions are written anywhere. Just f will do I think. For expample instead of:

“If you want to stick to integers, you can get the integer quotient and remainder with quot( ) and rem( ):”

I would prefer:

“If you want to stick to integers, you can get the integer quotient and remainder with quot and rem:”

Maybe use special typeface instead. This applies to other sections as well.

2008-11-14
39TYPO

(re-split #“\\\\W+” “A fine day it is”)

=>

(re-split #“\\W+” “A fine day it is”)

2008-11-14
74TYPO

newManifest = new Manifest®;

is translated as

(.Manifest r)

but should be (I believe)

(Manifest. r)

2008-11-14
77TYPO

“XML data is tree” probably should be “XML data is a tree”

2008-11-14
88TYPO

“reduce applies f on the first two argu-
ments in coll” probably should be
“reduce applies f on the first two argu-
ments in seq”

2008-11-14
92SUGGEST

“The Seq abstraction of first/next ”
probably should be
“The Seq abstraction of first/rest ”

2008-11-14
89OK

(for [binding-form coll-expr filter-expr? …] expr)

in the PDF, “binding” has a function-name syntax coloring (purple? burgundy?)

2009-02-27
90TYPO

In the description of lazy-cons and lazy-cat, “lazy-cons” has function-name highlighting, but “lazy-cat” does not. This is the example between the paragraphs beginning “Clojure provides several utilities…” and “The actual combination is lazy:…”

2009-04-03
93TYPO

re-matcher sample code uses a pattern of #“\\\\w+” rather than #“\\w+”.

2008-11-14
94TYPO

In the example of seq’ing over the file system, the example assumes that java.io.File has been imported, since it is using “File” without qualification.

2008-11-14
94TYPO

In the example of seq’g over the file system using seq, the example and results is shown as

(seq (.listFiles (File. “.”)) )
-> (./data_structures ./exploring ./interop …)

On my system (openSuSE 11.0, Linux) at least, the result is a list of java.io.File objects, which render as an unreadable object:

(seq (.listFiles (File. “.”)))
-> (#<./book> #<./jars> #<./test> …)

This may be a difference in how File objects are rendered, or one version may have returned filenames rather than actual File objects; the former seems more likely.

2008-11-14
79ERROR

The book says:
(rest {:fname “Stu” :lname “Halloway”})
-> ([:fname “Stu”])

but the result on my terminal is:
user=> (rest {:fname “Stu” :lname “Halloway”})
([:lname “Halloway”])

2008-11-14
106TYPO

“Expensive computations need to be execute in parallel on multiple cores (or multiple boxes) in order to complete in a timely manner.”

This should probably be “need to be executed” or “need to be able to execute”, as needed.

2008-11-14
66TYPO

“To time an operation, you can use the time.” should be “To time an operation, you can use time.”

2008-11-14
71TYPO

Paragraph 2: “In Java, you must provide an implementation of method on every interface you implement.” Should be “of every method on any interface”?

2008-11-14
82TYPO

Paragraph 3: “Sequences never change..” — Extra period.

2008-11-14
118TYPO

The second to last line on this page has “an err” instead of “and err”:
“such as the standard I/O streams in, out, an err. Dynamic bind-”

2008-11-14
84SUGGEST

“(def whole-numbers (iterate inc 1))”

I think it would be a good idea to mention here or in section 4.3 that retaining a reference to the start of an infinite sequence might cause excessive memory consumption. Infinite sequences should mostly be used as streams which can be garbage collected as you move along. A single reference like “whole-numbers” can prevent this. So I think some advice could be helpful for your audience.

2009-01-29
7979ERROR

…towards the bottom of the page,

The response from first and rest are reversed.

should be:

(first {:fname “Stu” :lname “Halloway”})
-> ([:fname “Stu”])
(rest {:fname “Stu” :lname “Halloway”})
-> [:lname “Halloway”]

2008-11-14
100TYPO

(def song {:name “Agnus Dei”
:artist “Krzystof Pendereci”: Should be: “Krzysztof PenderecKi”

2008-11-14
79SUGGEST

Ahh I see, this may be an implementation detail since it’s a map.

2008-11-14
26SUGGEST

Although it has already been shown how to fetch and build Clojure, it hasn’t been shown how to do the same for clojure-contrib (only how to verify it’s in your classpath). It would be helpful to add instructions on how to do so.

2008-12-02
114TYPO

(guess-pi (run-simulation 100))

should be

(guess-pi (run-simulation 10))

you are talking about 10 iterations

2008-11-14
119TYPO

The code for calls-slow-double is missing, and the code for slow-double is printed twice:

Next, write a function named calls-slow-double that calls slow-double for
each item in [1 2 1 2 1 2]:
(defn slow-double [n]
(Thread/sleep 1000)
(* n 2))

I guess calls-slow-double should be something like:
(defn calls-slow-double [coll]
(map slow-double coll))

2008-11-14
122TYPO

Last sentence: “In the next chapter, you will see how Clojure
is bring macros to mainstream programming.” — Should be “is bringING macros”.

2008-11-14
66TYPO

When benchmarking, you tend to want to take several measure-
ments so that you can eliminated startup overhead plus any outliers, …

I believe you wanted eliminate?

2008-11-14
66TYPO

(faster-sum-to 10000) => (integer-sum-to 10000)

2008-11-14
71TYPO

#(fn [] => #(

2008-11-14
80ERROR

(conj to-sequence from-sequence) => (into to-sequence from-sequence)

2008-11-14
85TYPO

Now that you have the basics of creating sequences, you can use other Clojure …TRUNC

<p>Filtering Sequences</p>

Clojure provides a number of functions that filter a sequence, returning a sub…TRUNC

(filter pred coll)

2008-11-14
87ERROR

some returns the first true value for its predicate, or nil if no element
matched.
(some even? [1 2 3])
-> true ;;;;;;;; <
(some even? [1 3 5])
-> nil
Notice that some does not end with a question mark, since it is not quite
a predicate. some retur ns the actual value of the first match instead of
true.

user> (#{1 2 3} (some even? [1 2 3]))
nil

2008-11-14
91ERROR

(def x (for [i (range 1 10)] (do (println i) i)))
#’user/x

(doall x)
1
2
-> (1 2)

=>

user> (doall x)
1
2
3
4
5
6
7
8
9
(1 2 3 4 5 6 7 8 9)

2008-11-14
94TYPO

(re-seq \\w+ “the quick brown fox”)

=> (re-seq #“\\w+” “the quick brown fox”)

(sort (re-seq \\w+ “the quick brown fox”))
-> (“brown” “fox” “quick” “the”)
(drop 2 (re-seq \\w+ “the quick brown fox”))
-> (“brown” “fox”)
(map #(.toUpperCase %) (re-seq \\w+ “the quick brown fox”))
-> (“THE” “QUICK” “BROWN” “FOX”)

2008-11-14
98TYPO

(values map)

=>

(vals map)

2008-11-14
100OK

Is the color of this code strange, isnt’t it? (PDF version)

(assoc song :kind “MPEG Audio File”)
-> {:name “Agnus Dei”, :album “Polish Requiem”,
:kind “MPEG Audio File”, :genre “Classical”,
:artist “Krzystof Penderecki”}
(dissoc song :genre)
-> {:name “Agnus Dei”, :album “Polish Requiem”,
:artist “Krzystof Penderecki”}
(select-keys song [:name :artist])
-> {:name “Agnus Dei”, :artist “Krzystof Penderecki”}
(merge song {:size 8118166, :time 507245})
-> {:name “Agnus Dei”, :album “Polish Requiem”,
:genre “Classical”, :size 8118166,
:artist “Krzystof Penderecki”, :time 507245}

2009-02-16
108ERROR

(def messages (ref ()))

=>

(def messages (ref ’()))

2008-11-14
22SUGGEST

It is not clear how we get closure-contrib.jar. Earlier there was a “1.3 Running CLosure” that lists JDK and clojure.jar as the only necessary downloads. It would help if we section also included closure-contrib.jar. Unless we have this, on page 27, it is not immediately clear how we get that in order to add it to our classpath.

2008-11-24
28TYPO

probably a versioning thing, but

(take 10 introduction.fibs)

throws a ClassNotFound

(take 10 introduction/fibs)

works for me

2008-11-14
15ERROR

StringUtils::IsBlank() is missing a final “return true” as its last statement.

2008-11-14
53TYPO

Draft says: “Since Clojure can return null, the initial if is unncessary.”

Shouldn’t this be “Since Clojure can return nil …”?

Also, unnecessary is spelled wrong (missing e after nn).

2008-11-24
55TYPO

There is an visible in the output of your (find-doc) example. See last line below:

user=> (find-doc “reduce” )
————————————-
clojure/areduce
([a idx ret init expr])
Macro
… details elided …
————————————-
clojure/reduce
([f coll] [f val coll])
details elided …

2008-11-24
18TYPO

The given erratum is missing a ‘)’ at the end, so it should be:

(defn hello-world [name] (println (format “Hello, %s” name)))

2008-11-24
57TYPO

“Clojure’s Java support is the both powerful and lean.” should be “Clojure’s Java support is both powerful and lean.” I don’t think the “the” before “both” is correct.

2008-11-24
57TYPO

“Lean, in that can get right to the metal.” should be “Lean, in that it can get right to the metal.”

2008-11-24
61TYPO

“In Java, arrays are have their own syntax” should be “In Java, arrays have their own syntax”

2008-11-24
93ERROR

Regexp in re-matcher example has an escaped back-slash in it, meaning the Regexp doesn’t match the string, as of v1086

2008-11-24
114114TYPO

Clear the counter’s errors,
and verify that it state is the same as before the error occurred.

Should be:

Clear the counter’s errors,
and verify that it’s state is the same as before the error occurred.

2008-11-14
24SUGGEST

“The #{} is a literal for an empty set, since there are no visitors yet.” That sentence is confusing to me. Yes, “#{}” is the empty set literal, but that is not /because/ there are no visitors yet. I suggest you drop the second clause, and leave it at “The #{} is the literal for an empty set.”

2008-11-14
25TYPO

In the last paragraph on the page, you write “even though there are no locks, either directly in the code…” but isn’t dosync performing an implicit lock?

2008-11-14
83OK

For the range function, you may want to clarify somewhere (although the examples show this) that the value of the “end” argument does not appear in the sequence returned by range. I might have suggested using “limit” as a replacement for “end”, if it weren’t already so coded in Clojure itself.

2009-01-29
45ERROR

The sentence: “When you create a new namespace, the java.lang package and the Clojure namespace are automatically available to you” is not correct. If you create a new namespace with “ns” the Clojure namespace is available in the new namespace; however, if you create the new namespace with “create-ns”, the Clojure namespace is NOT available in the new namespace.

2008-11-14
35TYPO

Only nil? is nil, and only false is false?.

should read:

Only nil is nil?, and only false is false?.

2008-11-14
18TYPO

s/Lisp/Clojure/ in “Lisp doesn’t care about commas
anyway, and adding commas can make some data structures
more readable. Consider vectors:”

2008-11-14
81TYPO

Joe Asks last paragraph: “When you ask the REPL to display
a sequence, all it knows is that is has a sequence.” should be “When you ask the REPL to display
a sequence, all it knows is that it has a sequence.”

2008-11-14
96TYPO

First paragraph, “Then, create a clojure-loc function then counts…” should be “Then, create a clojure-loc function that counts”

2008-11-14
112SUGGEST

Maybe “you can send the agent a function to update its state” is enough explanation for some (along with “send tells the agent to update itself on a thread pool”), but not for me. I suggest that you explain something about the interface of the function used to update the agent, rather than requiring the reader to infer it from examining run-simulation (because you’ve only touched upon multimethods by this point anyway).

2008-11-14
15SUGGEST

As per above, the two blank methods aren’t quite equivalent as the first accepts strings containing non-space whitespace characters. Perhaps replace (every? #{\\space} s) with (every? #(Character/isWhitespace %) s)? It reinforces Java interop as well.

2008-11-14
18ERROR

Should be:

(defn hello-world [name]
(println (format “Hello, %s” name))

I believe.

2008-11-14
42TYPO

“Functions are not the only way to have
create a lexical binding.”

“have” is unnecessary

2008-11-14
49SUGGEST

When talking about recur, I’d like to suggest that you talk briefly about tail recursion and tail call optimization. The JVM does not support TCO now but there seem to be strong indicators that it is coming which means that loop/recur might not be necessary in the future, leading to recursive code that looks closer to that written in other Lisps.

2009-01-29
14TYPO

“Lisp brings wisdom
spanning most [of] the history of programming, and Java brings the
robustness and tooling of the dominant platform available today.”

2008-11-14
22TYPO

Is there a reason to run the REPL with
java -cp clojure.jar clojure.lang.Repl
instead of the simpler
java -jar clojure.jar
They seem to do the same thing.

2008-11-14
112TYPO

Here’s my log:

user=> (def counter (agent 0))
#’user/counter
user=> (send counter inc)
clojure.lang.Agent@421ab0
user=> counter
clojure.lang.Agent@421ab0
user=> @counter
1
user=>

You had #=(var user/counter),
I had #’user/counter

2008-11-14
15TYPO

Along with what has already been said, I don’t think it makes sense for nil to be counted as a “blank” string. To me nil represents the absence of anything. I understand that you are just trying to replicate StringUtils functionality, but you may want to pose the question “Is a nil string really a blank string?” It may deter from the point of that section, but just wanted to give my .02 cents.

2008-11-14
18SUGGEST

The choice of wording when talking about commas is a little confusing. Maybe it should read something more like “Clojure doesn’t need the commas, but allows them for the purpose of readability.”

2008-11-14
50SUGGEST

On the very first sentence of this page, I would suggest changing “The wikipedia” to either “Wikipedia” or “The Wikipedia article on metadata”.

2008-11-24
19TYPO

It’s not “Guiseppe Verdi”, but “Giuseppe Verdi”.

2008-11-24
22TYPO

Using the latest clojure source (13th Nov 08) on my Windows XP pc, the output of the “hello” function definition is not:

#=(var user/hello)

but

#’user/hello

2008-11-24
65TYPO

Idiomatic Clojure takes advantage of duck typing, but you can
add type hints Section 3.2, Adding Type Hints, on page 68 for
performance or documentation purposes.

This should be:

Idiomatic Clojure takes advantage of duck typing, but you can add type hints. See Section 3.2, Adding Type Hints, on page 68 for performance or documentation purposes.

2008-11-24
34SUGGEST

It wasn’t quite clear at first why exactly you had to use (apply str …) in order to get a string back. I thought just calling (str (interleave …)) would work. It took a few doc calls and a couple of minutes to realize that interleave created a sequence, and that apply is needed so that you can work on the sequence. By just calling str I was calling toString on a sequence object (I’m not even sure if that makes sense, but it’s how I understand it). Anyways, you may want to clarify what is going on here. Unless I’m just a slow learner.

2008-12-02
26TYPO

The command to fetch Clojure-contrib from svn is incorrect. Instead of:

svn co -r 240 svn co clojure-contrib

It should be:

svn co -r 240 clojure-contrib

2008-11-24
26TYPO

The classpath used for clojure includes directories that don’t exist (e.g. /book/examples doesn’t exist in the given code). It should be:

export CLASSPATH=/jars/clojure.jar:\\
/jars/clojure-contrib.jar:\\
/book:/examples
java clojure.lang.Repl

2008-11-24
27SUGGEST

Was previously able to run the book examples with B1.0 but cannot for B2.0. (require ’introduction) still returns nil as it should but cannot call fibs:

(take 10 introduction/fibs)
java.lang.Exception: No such namespace: introduction (NO_SOURCE_FILE:7)

Also, at the bottom of the page the same example is used but with introduction.fibs instead of introduction/fibs. Is one of these a typo?

2008-11-24
64SUGGEST

In Figure 3.1, the Sugared column’s contents go off the page and are unreadable.

2009-04-29
89TYPO

Instead of {:a 1 :b 2 :c 3} it should be {:a=1 :b=2 :c=3}.

2008-11-24
14TYPO

“Clojure is Elegant”: In title case, always capitalize “Is”. This error appears in headers throughout Chapter 1.

2009-02-16
25TYPO

“This helps hello will scale well to multiple processors.” Two competing clause structures here. Pick one. :-)

2008-11-24
27ERROR

Re: the problem with running (take 10 introduction/fibs), the problem appears to be namespace-related. In the source code file book/examples/introduction.clj, the first line is “(ns examples.introduction).” I changed it to “(ns introduction)” and everything worked fine.

BTW, (take 10 introduction/fibs) works fine, but (take 10 introduction.fibs) does not.

2008-11-24
88ERROR

In the example for using first and rest on a map, the return values are reversed. The text reads:

(first {:fname “Stu” :lname “Halloway”})
-> ([:fname “Stu”])

(rest {:fname “Stu” :lname “Halloway”})
-> [:lname “Halloway”]

It should be the other way around:

(first {:fname “Stu” :lname “Halloway”})
-> [:fname “Stu”]

(rest {:fname “Stu” :lname “Halloway”})
-> ([:lname “Halloway”])

2008-11-24
35SUGGEST


“This is a\
multiline string”
-> “This is a\
multiline string”

At this point the reader, who was expecting \
to introduce a line-break, is surprised to find out that this isn’t the case. And the next example confirms that something weird is going on:


“This is also
a multiline String”
-> “This is also\
a multiline String”

At this point, we kind of expect an explanation as to why clojure seems to always escape line breaks, transforming actual line feeds into \
in the second example, or not expanding \
in the first, so expectations are raised …

And indeed the next paragraph starts with:

Clojure does not wrap most of Java’s string[…]

Aha, we’re getting there …


[…]string functions. Instead, you can
call them directly using Clojure’s Java interop forms:
(.toUpperCase “hello”)
-> “HELLO”

Talk of a let down!

We were led to expect the explanation about this \
weirdness only to be completely left hung and dry with unrelated Java interop stuff!

The java interop stuff surely has its place in the book, but not by using the word “wrap” within eye distance of the \
mystery …

:-)

2009-02-16
39SUGGEST


If a function is a predicate, then by convention its name should end with
a question mark. As an example, the predicates below test the type of
their argument, and all end with a ?:
user=> (string? “hello”)
true
user=> (keyword? :hello)
true
user=> (symbol? ’hello)
true

Fine so far … except that the next paragraph throws through a loop:

To define your own functions, use defn:

(defn name doc-string? attr-map? [params*] body)

Aha? so doc-string? and attr-map? are predicates, right?

Nope. Since you are already using * with meaning zero or more in “[params *]”, why not use this very same convention everywhere instead:

(defn name doc-string* attr-map* [params*] body)
2008-11-24
21ERROR

The last line of this page says the book targets subversion revision 1087, but the preceding command line instructions for checking out and building the clojure source code use revision 1097.

2008-11-24
51TYPO

“The first time through, loop binds result an empty vector, and …”

This should read

“The first time through, loop binds result to an empty vector, and …”

instead

2008-11-24
62TYPO

import takes a variable number of lists, with the first part OF each list being
a package name

2008-11-24
34SUGGEST

You list +, -, *, /, !, ?, ., and _ as characters that can appear in symbols, then immediately show us >, <, =, and >=. I don’t know if the first list is incomplete or Clojure is currently being more permissive than it intends, but right now:

user=> (def oh> “oh greater than”)
#’user/oh>
user=> oh>
“oh greater than”
user=> (def o=h “o equal h”)
#’user/o=h
user=> o=h
“o equal h”

If those shouldn’t work, it would be good to point out that Clojure gets to use a handful of symbols we don’t. If they should, then they should go on the list (even though they’re much less likely to be useful than the characters you list now).

Thanks.
-hume.

2009-02-16
37TYPO

“So far, you have seen numeric literals, lists, symbols, strings, charac-
ters, booleans, and nil.”

We’ve also seen vectors.

2008-12-02
39SUGGEST

The use of “(defn name doc-string? attr-map? [params*] body)” for showing the shape of a form is surprising here, since we haven’t seen it yet, and it’s styled just like actual code samples.

In this case you could show us an example with trailing comments on the doc-string and attr-map lines letting us know those are optional, but presumably that’s going to get onerous before long (not to mention we don’t know what an attr-map looks like yet). Probably there ought to be distinct styling with an explanation beforehand as to what that styling means.

2008-11-24
115TYPO

Line 3lets a pro

Missing a space.

2008-11-24
47TYPO

Third paragraph (in-ns REPL example); the name of the name namespace should be quoted, i.e. user=> (in-ns myapp) should read: user=> (in-ns ’myapp)

2008-11-24
47ERROR

“When you create a new namespace with in-ns, the java.lang package and
the clojure.core namespace are automatically available to you…”; this is wrong: newly created namespaces contain only mappings for the classnames in java.lang.

2008-11-24
104SUGGEST

In order to use the duck streams they must first be imported from clojure-contrib:

(use ’clojure.contrib.duck-streams)

2008-11-24
109TYPO

• assoc returns map with a key/value pair added should be • assoc returns a map with a key/value pair added

2008-11-24
27ERROR

Because of the namespace change,
(take 10 introduction/fibs)
should now be
(take 10 examples.introduction/fibs)

2008-11-24
94SUGGEST

In the list of data constructors, “set” is used to construct sets, but it is stated that its argument list is different than list, vector, and hash-map. Function hash-set is the way to construct sets and it is consistent with other constructors in the way it treats its arguments. Perhaps “set” should be mentioned as an additional function to create sets.

2008-11-24
128OK

There is currently no run-simulation function in concurrency.clj, so it is not possible to follow along in this section yet. Perhaps add it in or make a note of it for the interested reader?

2009-01-29
131TYPO

The code at the top of the page defines backup-agent, which was already defined on the previous page. The context implies that add-message-with-backup should be defined here instead (since it doesn’t get defined later and is needed at this point).

2008-11-24
47TYPO

(in-ns myapp) throws java.lang.Exception: Unable to resolve symbol: myapp in this context (NO_SOURCE_FILE:19)

whereas

(in-ns ’myapp) works just fine

2008-11-24
33TYPO

Missing space before the \\1 in “as opposed to the
more familiar infix notation1 + 2 = 3.” :)

2008-11-24
7TYPO

The font of the first 3 paragraphs on page 7, up to the footnote numeral, displays in an unreadable squashed-thin font in Okular (Linux KDE4 PDF viewer). This also occurs in various other places in the PDF.

2009-05-27
114TYPO

“You are going to use this object several times, so seq it and stuff [it] into a var named prop-descs:” (“in” should be “it”)

2008-11-24
89SUGGEST

I think the statement “conj adds a single item to a collection” may be slightly misleading/confusing. I read that as “you can only insert one item at a time with conj.” It seems like the real distinction you are trying to make is that conj adds an element “as-is” to the sequence. So if you conj a set to a sequence it will keep the set “intact.” Whereas, if you do into it will pull each element from the set (i.e. sequence) and “put it into” the target sequence. I realize I’m using bad verbiage since everything is immutable, but I think I made my point clear.

2008-12-02
82ERROR

Apparently the call to init on line 11 of the ant-project def requires ant-launcher.jar be included in the classpath in addition to ant.jar.

Error:
java.lang.NoClassDefFoundError: org/apache/tools/ant/launch/AntMain

apache-ant-1.7.1

2008-11-24
30TYPO

I think

(javac {:srcdir src :destdir dest}))

is supposed to be

(javac {:srcdir src :destdir build}))

2008-11-24
38TYPO

In this list

Form Example(s) Primary Coverage
Boolean true, false Section 2.1, Booleans and Nil , on page 36
Character \\a Section 2.1, Strings and Characters, on page 35
Keyword :tag, :doc Section 2.1, Forms, on page 32
List (1 2 3), (println “foo”) Chapter 4, Unifying Data with Sequences, on page 86
Map {:name “Bill”, :age 42} Chapter 4, Unifying Data with Sequences, on page 86
Nil nil Section 2.1, Booleans and Nil , on page 36
Number 1, 4.2 Section 2.1, Using Numeric Types, on page 33
Set #{:snap :crackle :pop} Chapter 4, Unifying Data with Sequences, on page 86
String “hello” Section 2.1, Strings and Characters, on page 35
Symbol user/foo, java.lang.String Section 2.1, Using Numeric Types, on page 33
Vector [1 2 3] Chapter 4, Unifying Data with Sequences, on page 86

You say that Keyword & Symbol are covered, but I don’t see the coverage.

2009-01-13
82TYPO

“representation of a Projectdoes not tell you”
Projectdoes -> Project does

2008-11-24
68ERROR

dotimes requires a vector for init condition. Text has:
(dotimes _ 5 (body))
Should be:
(dotimes [_ 5] (body))

2008-11-24
68SUGGEST

sum-to examples can be more consistent, even if functionally equivalent.
sum-to initializes with i = 1
integer-sum-to with i = 0
unchecked-sum-to with i = 0

2008-11-24
28ERROR

Get Java exception java.lang.Exception: namespace ‘introduction’ not found after loading ‘/introduction’ after entering (use ’introduction).
Possibly due renaming namespace (changing (ns examples.introduction) to (ns introduction) in introduction.clj fixes the problem).

2008-12-02
41TYPO

Lower third of page: “self-explantory” should be “self-explanatory”

2008-12-02
42TYPO

“There is an ever shorter syntax” should be “There is an even shorter syntax”

2008-12-02
47TYPO

Very top: the second occurrence of “foo” should be formatted like the first one

2009-02-16
82TYPO

In the bullet list under the source code for the ant-project definition: “The proj on line 10 is the return value of the let…” I think should read “The proj on line 13 is the return value of the let…” since line 13 reads “proj))”. That looks like the return value to me.

2008-12-02
92TYPO

Sentence “The replicate function replicates an element xn times.” is missing a space between x and n, it should read
“The replicate function replicates an element x n times.”

2008-12-02
92TYPO

Sentence “The replicate function replicates an element xn times.” is missing a space between x and n, it should read
“The replicate function replicates an element x n times.”

2008-12-02
92TYPO

Sentence “The replicate function replicates an element xn times.” is missing a space between x and n, it should read
“The replicate function replicates an element x n times.”

2008-12-02
133TYPO

extra “this” in “Calculations like slow-double this are good candidates for memoization.”

2008-12-02
84ERROR

Using (doto task …) will already return task, it’s not necessary to explicitely return it from the function.

2008-12-02
53SUGGEST

This section is puzzling to me

"This makes sense if you go though it step by step. The metadata macro
creates a symbol stu with metadata attached. Then, the symbol gets
evaluated, and the value of stu is bound to serializable-stu. The metadata-
attached symbol stu is never assigned to anything, and is lost.

When you create a new object based on an existing object, the existing
object’s metadata flows to the new object."

I feel like the start of the second P contradicts the first. I think I understand what’s meant now, but only after reading a separate blog article on the topic

2009-01-13
66ERROR

“…such as to-array, which creates an array directly from any sequence.” You then show to-array passed a vector, which seq? tells us is not a sequence. It seems like there’s a distinction that should be made between things that really are sequences and things that can have sequences wrapped around them by clojure when necessary.

2008-12-02
46SUGGEST

I’m finding that I want more explanation of a few core things. For example, what’s a symbol and what’s a keyword? I know you want to gloss over now and dig into detail later. But, at this stage of the book I’m toying on my own and not knowing some of the core things is frustrating.

I could read ahead, but I prefer to read linearly, and I expect I’m not alone there.

I think the best solution to my concerns is put something short and sweet on everything from the lists on Figure 2.1 and Figure 2.2.

2009-02-16
41OK

In the anonymous functions examples I found I was using the #body syntax, is there a reason that you use the (fn [] #body) syntax instead?

2009-02-16
23SUGGEST

You might want to mention how to get out of REPL.

2008-12-02
21SUGGEST

You might want to emphasize more how important it is that you grab the svn revision instead of the last release available as a download. Something along the lines of “The examples in the book will not work if tried on the September 2008 release of Clojure” would probably be sufficient.

2008-12-02
47ERROR

I get

user=> (in-ns ’myapp)
#

instead of

user=> (in-ns ’myapp)
nil

2008-12-02
47TYPO

String works fine but doc doesn’t

myapp=> String
java.lang.String
myapp=> #’doc
java.lang.Exception: Unable to resolve var: doc in this context (NO_SOURCE_FILE:0)

However, doc seems to be available when fully qualified

myapp=> ’clojure.core/doc
clojure.core/doc

2008-12-02
52TYPO

To test reference quality,

should be

To test reference equality,

2008-12-02
92TYPO

Missing space in
“that consconstructs a new sequence”

2008-12-02
92SUGGEST

Last example, using cons with a map.

The end result is a seq of key/value pairs? Is there an easy way to turn this back into a map? I think a little more detail on this would be worthwhile.

2008-12-02
93TYPO

“but that order depends on implementation detail and”

Should that be “on an implementation detail” or “underlying implementation details”?

2008-12-02
59TYPO

shouldn’t

(and (get {\\b \\y} \\z) 0)

be
(and (get #{\\b \\y} \\z) 0)

2008-12-02
153TYPO

The IPersistentVector example code uses java.util.Collection, not IPersistentVector. Its the same as the previous example.

2008-12-02
18TYPO

missing closing parenthesis in (defn hello-world …

2008-12-02
159TYPO

first paragraph: “We have already covered it in detail …”

2008-12-02
174OK

On Paul Barry’s recommendation, I’m using AquaMacs: h_ttp://aquamacs.org

h_ttp://paulbarry.com/articles/2008/07/02/getting-started-with-clojure-and-aquamacs

2009-02-16
24SUGGEST

Please explain how to do this: “you can test your install by navigating to the directory where you placed clojure.jar, and running the Clojure Read-Eval-Print Loop (REPL).” I use a script suggested by Paul Barry: ht_tp://paulbarry.com/articles/2008/07/02/getting-started-with-clojure-and-aquamacs

2008-12-02
67TYPO

Figure 3.1 runs off the page

2009-04-29
119TYPO

In the code for set-property!, for a readable error message there should be a space at the end of “No such property” as there is in “No task named ” on the next page.

2008-12-02
125TYPO

I find this frustrating: “Notice how the Clojure model fits the real world.” In the current-track example the Clojure model fits the real world, but I can imagine lots of examples where it doesn’t (for example, incrementing a playcount).

Maybe STM is most appropriate where the situation being modeled maps intuitively to repointing refs, but if that case exists to be made, you haven’t made it yet. If it’s worth saying, how about “Notice how in this example the Clojure model…”?

2008-12-02
59SUGGEST

If I’m predisposed to hating lisp, I’m going to look at this example and say: “All the work is done in the indexed function, but the author isn’t counting those lines, branches, etc.” Recommendation: ditch indexed and use indexed’s map function in index-of-any. You get the same stats, but are free from the objection.

2008-12-02
16OK

“expressions begin with a verb”

I would not use the term “verb” here, but rather “operator” since “verb” does not sound familiar to many programmers. However I think most programmers reading this book know what functions and operators are. Maybe some reference to mathematical notation which also puts function names before its arguments might be illuminating. A simple statement that Lisp consistently does the same for operators that are put in infix position in math and one or two examples should make this sufficiently clear for everybody.

2009-01-29
109TYPO

Last sentence on the page: “to find out much” should be “find out how much”

2008-12-02
113TYPO

Bullet-point “merge”: “If multiple maps contains” should be “If multiple maps contain”

2008-12-02
95TYPO

“The functions below are grouped into three broad categories:”, but four categories are listed.

2008-12-02
96TYPO

“The replicate function replicates an element xn times.” needs a space between “x” and “n”.

2008-12-02
102TYPO

displayed result of the (for [word… comprehension adds an extra “>” character after each word as:
-> ("

the>

" "

quick>

" "

brown>

" "

fox>

")

2008-12-02
92ERROR

(first {:fname “Stu” :lname “Halloway”})
-> ([:fname “Stu”])
(rest {:fname “Stu” :lname “Halloway”})
-> [:lname “Halloway”]

this is wrong. first doesn’t return a seq, but rest does. Should be:

(first {:fname “Stu” :lname “Halloway”})
-> [:fname “Stu”]
(rest {:fname “Stu” :lname “Halloway”})
-> ([:lname “Halloway”])

2008-12-02
45TYPO

“There is an ever shorter syntax” -> “There is an even shorter syntax”

2008-12-02
43TYPO

The function “greeting” is defined with a single parameter … “username” … along with a documentation string describing the function.

When the “doc” function is invoked upon greeting, the output contains “name” instead of “username” both in the documentation string and in the parameter list.

The paragraph on the same page describing arity also refers to “name” instead of “username” .

2008-12-02
78TYPO

“This makes is very easy …” should be
“This makes it very easy …”

2008-12-02
55TYPO

In the last sentence, “useidentical?” should be “use identical?”. Note the space added.

2008-12-02
29SUGGEST

I’m trying to get clojure-contrib to be recognized. Please indicate what directory you are in to run svn. More detail on how CLASSPATH is interpreted would help (not every reader is up-to-speed with Java and Ant). Please give a concrete example showing the expected directory structure. Is there some way to see what directories (require ) searches?

2008-12-02
59SUGGEST

“The inner get checks” and “[…] the some function calls[…]”. I would suggest changing the font for both “get” and “some” or use quotes of some sort as the sentence is not grammatically correct otherwise. (I had to read “the some function calls” thrice before understanding that what was meant was “the some function calls”.)

2008-12-02
60SUGGEST

The discussion of the merits of the functional vs imperative version of ’index-of-any" should also point out that the imperative version can be seen as more efficient when a match is found because scanning stops right there, whereas “indexed” constructs the whole list of pairs, regardless of whether or not a match WILL be found. Then a balancing consideration of whether this efficiency loss does or doesn’t matter should also be included. Just to stay on the fair side of the fence :)

2008-12-02
125OK

Something I’ve been struggling with in the presentation I’ve been assembling: more realistic examples of where you would use agents and STM.

From my read of how per-thread Vars work, you should be able to override the implementation of any method using a Var, and interesting possibility, especially for testing.

2009-01-29
21TYPO

“…These features makes Clojure code less list-y…”
These features make

2009-01-29
75TYPO

I get

user=> (describe-class “foo”)
java.lang.ClassCastException: \\

but I don’t see

java.lang.String cannot be cast to java.lang.Class

did I miss a setting?

2009-01-13
77TYPO

you don’t need the commas in the args for startElement

(startElement [qname local qname atts]

If they are there for clarity, I think the example would benefit from calling that out.

2009-01-13
78OK

While working with the following code

(dotimes [i 5]
(.start
(Thread.
(fn []
(Thread/sleep (rand 1000))
(println (format “Finished %d on %s” i (Thread/currentThread)))))))

I tried to use the #(body) anonymous function form, which I couldn’t get to work. I noticed that doing two things (sleep & print) wasn’t going to work well for me. (or maybe I missed the obvious way to do it). Once I noticed that I was trying to do two things, it felt very similar to the section that talks about side effects and (do …). It might be worth pointing it out and discussing how #body doesn’t allow you to execute multiple functions (seamlessly), but fn [] body does. And, whether or not you think that’s a good thing.

2009-02-16
80TYPO

for those of us who haven’t had to deal with Java classpaths in the past, getting this running was quite a pain. I think a few more hints would be helpful. Just telling me that you have $CLOJURE_CLASSPATH setup wasn’t super helpful. It turned out I was missing a colon at the end of my classpath string.

2009-01-13
80SUGGEST

It’s not immediately obvious to me why the [_] is needed in

(defn WidgetTest-testOne [_]
(Assert/assertEquals 1 1))

Why would an argument be passed to a test function?

2009-01-29
82SUGGEST

an example of using (with-open) would be helpful.

2009-01-29
83SUGGEST

I get to Adding Ant Projects and Tasks to Lancet and I don’t have ant.jar. I’m on a plane, so I’m not getting ant.jar right now. I’m headed to a family members house a bit in the country where I won’t have internet for a week. That’s a bit of a bummer. Back in the intro when I got Clojure, it would be nice if it also said “If you want to download all that you’ll need for this book, you’ll also need clojure-contrib and ant.” Directions could be inline, or you could give a page number and leave the directions where they are.

2009-01-13
84OK

I found myself wanting to do (Mkdir/class) instead of (class mkdir-task), but I got an error. So I tried something even simpler.

user=> (BigDecimal/class)
java.lang.NoSuchFieldException: class (NO_SOURCE_FILE:278)

I assume this is because there’s some Java magic that makes .class look like a Field when it’s actually not, but that’s not immediately obvious to non-Java programmers.

2009-02-16
95TYPO

The functions below are grouped into three broad categories:
• functions that create sequences
• functions that filter sequences
• sequence predicates
• functions that transform sequences

That reads strangely. Did you mean four categories? Or are you assuming zero based index? =)

2009-01-13
104TYPO

I think

(def x (for [i (range 1 2)] (do (println i) i)))

is supposed to be

(def x (for [i (range 1 3)] (do (println i) i)))

since

user=> (range 1 2)
(1)
user=> (range 1 3)
(1 2)

2009-01-13
114TYPO

(def inventors {“java” “gosling”, “clojure” “hickey”, “ruby” “matz”})

doesn’t seem to need the commas, tho I can see why they might add clarity. If that’s why you use them, I’d point that out, since they aren’t strictly required.

2009-01-13
114SUGGEST

Why create (def inventors {“java” “gosling”, “clojure” “hickey”, “ruby” “matz”}) (which is a hash, not a set) and never use it?

2009-01-13
115TYPO

I think
(select #(= 1 (.length %))) languages)
is supposed to be
(select #(= 1 (.length %)) languages)

2009-01-13
132TYPO

Shouldn’t

(clear-agent-errors counter)
-> nil
@counter
-> 1

be

(clear-agent-errors counter)
-> nil
@counter
-> 0

2009-01-13
135SUGGEST

I felt like the monte-carlo simulation was way too large a step from the previous simple agent examples. Something with far less complexity is probably much more instructive.

2009-01-29
40SUGGEST

“Other than false, nil also evaluates to false” - I think “Other” is an awkward word there; “In addition to false,” would be clearer.

2009-01-13
40TYPO

“Other than false and, nil, …” - remove comma after “and”

2009-01-13
42SUGGEST

“If a function is a predicate…” - I don’t think you’ve defined what a predicate is at this point.

2009-01-13
46TYPO

When to use anonymous functions: “called from more than once place”: replace “once” with “one”

2009-01-13
31ERROR

Reloading Changed Libraries
….

(use :reload ’introduction)
(require :verbose ’introduction)
(use :reload :verbose ’introduction)

; clojure svn revison 1131 wants:

(use :reload ’examples.introduction)
(require :verbose ’examples.introduction)
(use :reload :verbose ’examples.introduction)

2009-01-13
28TYPO

“On 2, hello checks to see if name is already in the set” -> “if username is already in the set”.
“On 3, commute updates the visitors to include the name name” -> “updates visitors to include the name username” or “adds the name username to the set to which visitors refers”.

2009-01-13
29ERROR

In clojure r1097, require gives additional (and useful) feedback:
user=> (require :verbose ’clojure.contrib.str-utils)
(clojure.core/load “/clojure/contrib/str_utils”)
nil

2009-01-29
37SUGGEST

“As you can see, Clojure has a built-in Ratio type” — could demonstrate this:
user=> (/ 22 7)
22/7
user=> (.getClass *1)
clojure.lang.Ratio

2009-01-13
53TYPO

“Clojure can afford to have a small set of flow
control forms, because you can use use macros to add your own.”

should only be one ‘use’

2009-01-13
42TYPO

Figure 2.2: Reader Macros

“Regex Pattern #”foo" => a java.util.regex.Pattern Section 4.4, Seq-ing Regular Expressions, on"

page ### is not given and/or improperly formatted

2009-02-16
17TYPO

What is “threaded member access”? Why not do something simpler to start?

2009-01-13
50-51ERROR

pp 50-51 (prob. a W32 diff.)

myapp> java.io.File/separator
=> “\\\\”
myapp> (import ’(java.io File))
=> nil
myapp> File/separator
=> “\\\\”

2009-01-13
55TYPO

The last sentence, “To test reference quality, use identical?:” should be:
“To test reference equality,…”
“equality” instead of “quality”

2009-01-13
56TYPO

“This makes sense if you go though it step by step.”
should be:
“This makes sense if you go through it step by step.”
“though” should be “through”.

2009-01-13
67TYPO

Section: Using Java Collections - Prgrph 2 last sentence:
“But you won’t have the benefit of Clojure’s function style.”

“Clojure’s functional style.”

2009-01-13
77TYPO

DefaultHandler#startElement accepts params named uri, localName, qName and attributes. You have duplicated the qName and ignored the uri in binding the params which caused slight confusion.

2009-01-13
114OK

The languages, letters, beverages, inventors example code appears to be laid out in some sort of table within the PDF.
This makes it impossible to copy and paste into the REPL.

2009-01-13
85ERROR

(def project (org.apache.tools.ant.Project.))
#=(var user/project)

should be (?)

(def project (org.apache.tools.ant.Project.))
#’user/project

2009-01-13
83SUGGEST

ANT is a ridiculous XMLism with no expressive power gained in exchange for the perversion of XML’s obfuscation. It seems newcomers to programming understandably acclimate to ANT for the simple reason that they haven’t used make, or are averse to make’s stupid tab syntax, whereas multi-decade Unixers see it for the ephemeral pop XML-culturalism it is. The book is fantastic.

2009-01-29
67ERROR

The snippet on the page reads:
(doto (System/getProperties)
(setProperty “name” “Stuart”)
(setProperty “favoriteColor” “blue”))

To make this work with clojure-svn as of Sun Dec 7 19:29:07 CET 2008 I had to add ‘-quotes:
(doto (System/getProperties)
’(setProperty “name” “Dag”)
’(setProperty “favoriteColor” “black as my soul”))

2009-01-13
47SUGGEST

With respect to the square-corners function.

For the developer who is not familiar with Lisp, this example isn’t completely clear. It took me a couple reads to realize that the let expression itself produces a value. This is not the first time that ‘let’ is used, but this is the section where ‘let’ is explained. It would be good to be more explicit about how the value of the let expression is calculated, possibly by showing the result of calling square-corners.

Really, it just seems like ‘let’ could use a better introduction somewhere.

2009-01-29
92TYPO

In the text bubble, the origin of Cons

“it is more accurate to say
that consconstructs a new sequence”

Missing a space cons Constructs a new sequence

2009-01-13
150TYPO

Presumably, the string in the (unless true…) example should read “this should not print”

2009-01-13
98SUGGEST

You show using the list, vector, hash-set and hash-map functions. I think it would be good to add the sorted-set and sorted-map functions.

2009-02-16
134TYPO

“You number will probably not match those below” should be “Your number will probably not match those below”

2009-01-13
155SUGGEST

“In the code above, the bolded portions are the body to be generated, and the remainder is the macro code.”

Even at 800% zoom, it’s hard for me to detect any bolding. Perhaps I’m looking at the wrong code, or bolding isn’t working and you need a different font instead.

2009-04-29
79ERROR

I think this is an error? qname is there twice in the parameter list.

(proxy [DefaultHandler] []
(startElement
[qname, local, qname, atts]
(println (format “Saw element: %s” qname)))))

This is at the top of the page.

2009-01-13
171TYPO

Notice the doubled parentheses. The inner parentheses call :has-run to
look up the function, and the outer parentheses call the function itself. Use has-run> to check that boo has already run:

Should be ‘has-run?’

2009-01-13
186TYPO

Per fectionist that you are, you cannot stand that vectors print with
rounded braces, unlike their literal square-brace syntax. So‘ add yet
another my-print method, this time to handle vectors. Vectors all imple-
ment a IPersistentVector, so this should work:

There’s an extra single quote after “So’”

2009-01-13
46TYPO

In the output of (doc greeting), the doc-string should be “Returns a greeting of the form ‘Hello, username.’”, not “…‘Hello, name.’”. It looks like the parameter name and doc string were changed, but the code wasn’t re-run.

2009-01-13
27SUGGEST

It would be nice to show how to create a struct and then access the fields after you introduce defstruct.

I can do:
(.hashCode (struct person “John” “Doe”))

But haven’t yet figured out how to get the slots, e.g., the following fails:
(person-first-name (struct person “John” “Doe”))

2009-01-13
39TYPO

You say:
Forms such as + are called symbols, and are used to name things. For
example, + names the function that adds things together. Symbols cannot
start with a number, and can consist of alphanumeric characters,
plus +, -, *, /, !, ?, ., and _.2

Given that how do we get >, <, = into symbols? Since you show (< 5 2) in your examples on this page, we know that those characters are allowed (or are they something that looks like a symbol but isn’t).

2009-02-16
52ERROR

In the code example at the top of the page, str-join isn’t imported in the use statement:

(use ’[clojure.contrib.str-utils :only (re-split)])

even though it is used 3 lines later:

(str-join " " [w1 w2 w3 “…” ])))

I believe you want this instead for the use statement:

(use ’[clojure.contrib.str-utils :only (re-split str-join)])

2009-01-13
156ERROR

Top (description of macroexpand): “until the returned form is longer a macro” should have a “no”

2009-01-13
52ERROR

For the ellipsize function definition I think you need to ‘use’ the str-utils str-join function also else you get this error “java.lang.Exception: Unable to resolve symbol: str-join in this context (NO_SOURCE_FILE:4)”. I worked properly when I added “(use ’[clojure.contrib.str-utils :only (str-join)])”.

2009-01-13
49ERROR

The ellipsize() function example has incorrectly added the contrib.str-utils library. It reads:
(use ‘[clojure.contrib.str-utils :only (re-split)])
but it should read:
(use ’[clojure.contrib.str-utils)
since we are using the str-join call in the ellipsize function.

2009-01-13
88ERROR

While working along with the clojure version suggested, the safe-instantiate-task function did not work, with the problem apparently being the function “throw-if.” I checked the api and special forms and could not find “throw-if”. I changed the code to
(defn safe-instantiate-task2 [project name]
(let [ task (.createTask project name) }
(if (nil? task)
(throw (IllegalArgumentException. (str “No task named ” name)))
(doto task
(.init)
(.setProject project))))

and this behaved as expected.

I also wrote a throw-if as follows:
(defn throw-if [pred exc exprs]
(if pred
(throw exc)
exprs ))

I found this possible problem on page 88 (Section 3.5) of my pdf, but the above errata for the page do not match the contents on my page.

2009-01-13
151TYPO

double colon at the end of the sentence: “Macros provide a layer of indirection, so that you can automate the common parts of any recurring pattern..”

2009-01-13
169TYPO

“… let’s use them to convert Lancet until a DSL.”

‘until’?? perhaps you mean ‘into’?

2009-01-13
169TYPO

“However, calling Lancet is clunky, and using it feels more like calling an API that writing a build script.”

‘than’ rather than ‘that’

2009-01-13
171TYPO

“… extracts the has-run-rn from its metadata …”

this should be the ‘has-run-fn’

2009-01-13
171ERROR

“(defn has-run? [v] ((:has-run (meta v))))”

i think this should be
(defn has-run? [v] ((:has-run-fn (meta v))))
to work with the definition on the previous page.

2009-01-13
174TYPO

“Make sure that the function itself and the has-run? and rest functions work as expected.”

‘reset’, not ‘rest’

2009-01-13
81TYPO

The “Creating Java Classes” section disappeared. It was in beta 3, but is now missing.

2009-01-29
20SUGGEST

It might be confusing to talk about “keys of a set” before having clarified what sets are. If readers are coming from languages like Python and JavaScript, they would be used to the word `keys’ being used in the “key/value” context of Python dictionaries or JS objects.

For readers like me, it would be clarification enough to point out that Clojure has collections like maps, sets, and vectors (as on p.29) before talking about sets.

2009-02-16
20SUGGEST

(top of page) It seems a tad confusing to call the first argument of commute `ref’ right after covering `ref’ as a function of its own.

2009-01-13
87TYPO

#=(var user/echo-task)

should be

#’(var user/echo-task)

2009-01-13
52TYPO

The definition of ellipsize needs re-split AND str-join from clojure-contrib str-utils:

(use ’[clojure.contrib.str-utils :only (re-split str-join)])

2009-01-13
12TYPO

Missing “that”:

Lancet is a Clojure-based build system [that] works with Apache Ant.

2009-01-13
15SUGGEST

Link problem: in the pdf, the grey boxes are not clickable; only the text within it is. I find myself clicking several times until I hit a letter (using evince in ubuntu to display the .pdf).

For example the box “Download examples/preface.clj”

2009-03-31
17SUGGEST

Notice that in macosx java 5 is preinstalled, and that newer versions come from developer.apple.com, not from sun.com.
There should be a link to a webpage that explains how to install java (although I agree it sounds ridiculous).

2009-01-29
17SUGGEST

Chapter one assumes one is using a terminal — since the introduction states this book is not for novices to programming languages, perhaps that’s ok.
For windows, it should be suggested to have cygwin installed.
A footnote to emacs setup would help, or just to overall setup such as clojure.org/getting_started

2009-01-29
19SUGGEST

‘coll’ is not obvious for ‘collection’. It wasn’t to me the first time I saw it, it isn’t still now—had to think 2 seconds. I suggest addding a table of common informal abreviations… or at least a footnote.

2009-01-13
23TYPO

Missing section number:
“… the instructions in Section , Downloading Sample Code, on page 15”

2009-03-20
24SUGGEST

The floating box for running unit tests is out of place. nothing else in the text refers to it.

2009-02-16
25TYPO

“examples.introdution” is missing a ‘c’

2009-01-13
44OK

Although rhickey doesn’t mention the dot ‘.’ as a reader postfix macro, thats who it looks to my perhaps misguided self:

user=> (macroexpand-1 ’(String. “hello”))
(new String “hello”)

The table doesn’t mention the prefix and postfix dots at all.

2009-02-16
43TYPO

Figure 2.1 overflows past the page margins (at least in ubuntu/evince). Plus, it’s not a figure but a table. Same with figure 2.2.

2009-04-29
44TYPO

Figure 2.2 overflows past the page margins (at least in ubuntu/evince). Plus, it’s not a figure but a table. Same with figure 2.1.

2009-04-29
63TYPO

Missing an ‘e’ in “unnecessary”:
“Since Clojure can return nil, the initial if is unncessary.”

2009-01-13
63TYPO

Misspelled “langauge”:
“It may become more idiomatic when closures are added to the langauge.”

2009-01-13
66TYPO

On the previous, title page, “Sun” is capitalized when it shouldn’t: it’s the star, not the company.

2009-01-13
66TYPO

Never heard of “statics” (plural) as an adjective:
“… accessing statics methods and fields.”

2009-01-13
67TYPO

Same again: never heard of “statics”. Perhaps you are using it as shorthand for “static fields and methods”? The text:
“The . special form works with fields as well as with methods, and with statics as with instances.”

2009-01-13
67TYPO

I suggest Math/PI (no parenthesis) as an alternative to (. Math PI). If clojure makes a point of having less parens and accessing static fields as variables in a namespace, let it show!

2009-01-13
68SUGGEST

I suggest adding a (note the dot at the end of ClassName) after ‘form’ in the sentence:
“you can use the ClassName. form.”
When trying to show clojure to a novice, he thought the ending dot was a typo.

2009-01-13
69TYPO

Figure 3.1 overflows the page width a lot on the right.

2009-04-29
69ERROR

“doto” needs revision: now it needs dots for each method call. (later on, on ant project, you use it with dots).

2009-01-13
71SUGGEST

The asterisk in the code font is not very clear, at least in ubuntu/evince. I’d suggest using a proper, courier-style asterisk (with latex, the impossible is possible).

2009-05-27
72TYPO

For mnemonics, should mention that ‘memfn’ means ‘member function’ or ‘method function’. Otherwise it’s just an arbitrary weird name.

2009-01-13
74SUGGEST

TDD note is unprofessional.

2009-01-13
77TYPO

Adding type hints: 3 purposes, not two. The second one is dual (you even added an ‘and’).

2009-01-13
77SUGGEST

The #^Class example is a bit ill-chosen, because it’s confusing: the description uses #^ClassName. Why not choose another example, showing for example optimization for #^String?

2009-02-16
78SUGGEST

“Calling Clojure from Java” is misleading. I can’t come up with a better title, but it needs to convey the idea that it’s not the java programmer who is doing the calling, but java libraries. Perhaps “Handling Java callbacks in Clojure” or something along this line.

2009-01-29
81ERROR

“Loading Clojure into a Running Java Application” is empty.

2009-01-29
82SUGGEST

try-catch is explained in “Cleaning up resources”, not in “Wrapping checked exceptions” — reads misplaced to me.

2009-01-29
83TYPO

Space needed between ‘r’ and ‘f’ of “jarfile”; even the font is different:
“Ant ships with ant.jar and ant-launcher.jarfile”

2009-01-13
91SUGGEST

Code block is broken in an ugly way, leaving one stranded line in the next page, after a floating text box.

2009-03-20
103TYPO

This sentence doesn’t make any sense to me. Where’s the gotcha? why?
“Because rank is listed to the right of file in the binding form, rank iterates faster.”

2009-01-13
105OK

4.4 bullet-list is a repetition of chapter 4 opening bullet-list. Plus it’s missing Strings and arrays (which, in java, are neither collections nor any of the other entries)

2009-01-13
108OK

Any reason not to use (memfn getName) instead of #(.getName ) ? Same for map #(.toUpperCase) above.

2009-01-13
59SUGGEST

Someone following through the documents in the REPL will have redefined serializable-stu via (def serializable-stu #^{:serializable true} {:name “stu”}). Subsequently, (def stu-with-address (assoc serializable :state “NC”)) will not have e-mail associated with it. Even so, the printed value is “user/stu-with-address”, not {:name “Stu”, :email “stu@thinkrelevance.com”, :state “NC”} as shown on page 59.

2009-02-16
68ERROR

Clojure’s logging message seems to have changed (.getLocation… blahblah) now returns:

#<URL file:<path-to/clojure.jar>>

2009-01-13
70TYPO

In the paragraph, “The odd output is courtesy…” toString()for" needs a space.

2009-01-13
58OK

“You can prove that stu and serializable-stu are different objects by calling identical?:”

But this is true even if I make a similar stu without any metadata:

user=> (def stu {:name “Stu” :email “stu@thinkrelevance.com”})
#’user/stu
user=> (def stu-too {:name “Stu” :email “stu@thinkrelevance.com”})
#’user/stu-too
user=> (identical? stu stu-too)
false

2009-01-13
20TYPO

The line
(dosync & exprs)
should have a blue background (showing a form for the first time). At least I don’t see a blue background in my viewer - Ocular.

2009-01-29
49TYPO

…needs to be called from more than on[c]e place.

2009-01-13
120TYPO

I can’t see any reason you’d want this

(.invoke (. pd getWriteMethod) inst (into-array [value]))))

instead of

(.invoke (.getWriteMethod pd) inst (into-array [value]))))

2009-01-13
143TYPO

The output of running these has changed from

-> #=(var user/println-once-agent)

and

-> #=(var user/println-once)

to

#’user/println-once-agent

and

#’user/println-once

2009-01-13
121TYPO

… function lets you pass keys as symbols instead of strings.

Are they symbols or keywords? Prior to this sentence I thought :foo was a keyword.

2009-01-13
73SUGGEST

The (bean ..) example doesn’t work with Java 6, at least not on the Mac. I don’t believe 6 directly supports DES/CBC/PKCS5Padding ciphersuites. I think you should find a less fragile bean example.

2009-02-16
139ERROR

(use ’[clojure.contrib.memoize :only (memoize)])
(defn demo-memoize []
(time
(dorun
(binding [slow-double (memoize slow-double)]
(calls-slow-double)))))

It looks like memoize has been moved from contrib to clojure.core.

2009-01-13
41OK

“…you are passing it a single argument which contains the argument list.” When you say the “the argument list” it somehow seems odd. Might it be better to say “a sequence”?

2009-01-13
41OK

Sorry, I wish I could edit my suggestion. Instead of “the argument list”, how about “…you are passing it a single argument which is a sequence containing the argument list.”

2009-01-13
41OK

Ignore my suggestions on this page. I was wrong and I just didn’t originally get what you meant by “sequence.” I think part of it is that I automatically started thinking about types. For example, ‘(interleave “Attack at midnight” “The purple elephant chortled”)’ returns a list. But you refer to it as a “resulting sequence.”

The reason that calling ‘(str (interleave "Attack at mi…’ doesn’t work as expected is that the one argument, a list of characters returned by interleave, is passed to the function str and is converted to a string just like it printed out previously, but it is preserved by escaping the slashes.

Apply as described seems a bit magical. But maybe it could be better explained than as “unrolling argseq into an argument list”. Really it is a bit like an in place re-write. It is equivalent to evaluating argseq to a value and then evaluating “(f resulting-value-of-argseq)”. i.e. The apply function places the passed in function at the head of the list created by evaluating argseq. And the newly created list is finally evaluated.

e.g.

user=> (interleave “blah” “blek”)
(\\b \\b \\l \\l \\a \\e \\h \\k)

(apply str (interleave “blah” “blek”)
is equivalent to
(str \\b \\b \\l \\l \\a \\e \\h \\k)
which returns
“bbllaehk”

2009-01-13
44OK

I was immediately curious what the quote function would be used for. The following would have been nice.

user=> (apply + (1 2))
java.lang.ClassCastException: java.lang.Integer (NO_SOURCE_FILE:0)

user=> (apply + ’(1 2))
3

2009-02-16
148TYPO

Line 2 “before using a macro” rather than “before use a macro”

2009-01-13
52TYPO

The use statement in the ellipsize example should be getting str-join as well e.g.
(use ’[clojure.contrib.str-utils :only (re-split str-join)])

2009-01-13
52SUGGEST

In the example..
(defn ellipsize [words]
(let [[w1 w2 w3] (re-split #“\\s” words)]
(str-join " " [w1 w2 w3 “…”])))

Wouldn’t it be slightly better to use “\\s+” for the regular expression, which wouldn’t add any extra complexity for the reader? That way something like the following would work as expected.

(ellipsize “The quick brown fox jumped over the lazy dog.”)

2009-01-29
53OK

You provide many small examples, why not provide an example as to why you should use the clojure.core name space? It would make it obvious to the reader why it is needed.

user=> (in-ns ’myapp)
myapp=> (* 3 3)
java.lang.Exception: Unable to resolve symbol: * in this context (NO_SOURCE_FILE:93)

myapp=> (clojure.core/use ’clojure.core)
myapp=> (* 3 3)
9

2009-01-29
54OK

(use :reload ’examples.exploring)

“use reload” appears to be an important and often used concept. Why not provide the smallest possible example of using it? Maybe I’ll give an example tomorrow when I’m not so tired.

2009-02-16
54SUGGEST

“If you make changes in a namespace and want to make those changes
available to a running program, add the :reload option to use.”

Maybe it is just me, but this could be misleading. Initially I thought this implied that changes made to a namespace in the REPL must some how be “exported” to the running program and that things do not take effect immediately to a program running in a separate thread.

Thus, I thought the redefinition of println-alt below would have no effect on the running function which takes 20 seconds to complete. I thought for the redefinition to take place, there had to be some type of export into the running space. Obviously, I was wrong.

I now take it that what you meant to say is… “If you make changes to a file associated with a particular namespace and you want to make those changes available to a running program, then add the :reload option to use it.”

(in-ns ’test)
(clojure.core/use ’clojure.core)
(defn println-alt [str] (println “….” str))
(defn print20times [toPrint]
(defn printx [num]
(println-alt (str toPrint " " (- num 1) " more times"))
(. Thread sleep 1000)
(if (> num 1) (printx (- num 1)))
)
(printx 20)
)

(in-ns ’user)
(.start (Thread. (fn [] (test/print20times “blek”))))
; wait a few moments. issue following statement
(in-ns ’test)
(defn println-alt [str] (println “.” str))

2009-02-16
59ERROR

problem with ‘(def serializable-stu #^{:serializable true} {:name “stu”})’

;first case shown
(def stu {:name “Stu” :email “stu@thinkrelevance.com”})
(def serializable-stu (with-meta stu {:serializable true}))
(= stu serializable-stu)
;true
^serializable-stu
;{:serializable true}

;problem case shown
(def serializable-stu #^{:serializable true} {:name “stu”})
(= stu serializable-stu)
;false <—- problem
^serializable-stu
;{:serializable true}

;suggested case
(def serializable-stu #^{:serializable true} {:name “Stu” :email “stu@thinkrelevance.com”})
(= stu serializable-stu)
;true
^serializable-stu
;{:serializable true}

2009-01-13
171TYPO

The meta key for has run is :has-run-fn, but the has-run? function is looking for has-run.

2009-01-13
62SUGGEST

It is a bit hard to show in this example, but since you are pointing out how things work in pieces you might try to show what “some” does. At the minimum you might describe it as a function that takes in a function as the first argument and the second argument as a list of arguments to be applied to the function.

For me, the two examples were illuminating.
(some (fn [str] (println str) 3) ‘(“blah” “blek”))
(some (fn [str] (println str)) ’(“blah” “blek”))

2009-02-16
71SUGGEST

(map (fn[x] (* x 3)) (range 1 11))

favor instead

(map (fn [x] (* x 3)) (range 1 11))

This would be match the style you have done so far throughout the book. While playing around I discovered that

(index-of-any“zzab”#{\\b\\y})

would have worked on an earlier page, but obviously few would do such a thing.

2009-01-13
31SUGGEST

Cross references to the sections about three items listed under “There are three reasons that Clojure can attract more interest now than functional languages have in the past:” would be useful for those who are curious about those aspects of Clojure.

2009-01-13
33ERROR

I get a different output to the anonymous function mid-page:

user=> (.start (new Thread (fn [] (println “Hello” (Thread/currentThread)))))
nil
user=> Hello #<Thread Thread[Thread-1,5,main]>

2009-01-29
82SUGGEST

I recommend the following as an example for “with-open”.

(import ’(java.io File FileInputStream))
(with-open [is (FileInputStream. (File. “adf-doesnt_exist”))] (.is read))

2009-01-29
89TYPO

for readers of the B4 (but covered by someone else on the previous page)… instantiate-task will fail as written. “throw-if” is not a valid function so far as I know. use the following instead.

(defn instantiate-task [project name]
(let [task (.createTask project name)]
(if (nil? task)
(throw (IllegalArgumentException. (str “No task name ” name))))
(doto task
(.init)
(.setProject project))))

2009-01-13
175TYPO

define-ant-task will create a function named after an Ant task. The
function will will create and execute an Ant task.

Remove extra will.

2009-01-13
176TYPO

Test define-ant-task by creating a mkdir task, and then calling mkdir:
(define-ant-task mkdir)

According to the macro there should be a second argument passed in. I believe it is the actual string name for the task (in this case “mkdir”).

2009-01-13
179TYPO

define-all-ant-tasks in the printed code has two implementations. Remove the first as it is incorrect.

2009-01-13
192TYPO

Clojure’s inspector library uses Swing to create simple views of data. You
can use it to get a tree view of your system properties:
(use ’[clojure.inspector :only (inspect inspect-tree)])
(inspect-tree (System/getProperties))
-> #<JFrame …>
inspect-tree retur ns (and displays) a JFrame with a tree view of anything
that is treeish. So, you could also pass a nested map to inspect-tree:
(inspect {:clojure {:creator “Rich” :runs-on-jvm true}})
-> #<JFrame …>

Did you mean inspect-tree in the second example?

2009-01-13
190TYPO

:acc/Checking in service-charge example is missing a colon

2009-01-13
92TYPO

“In the output above, the result of rest or cons is a seq, not a vector.” Don’t you mean “not a list” instead of “not a vector”? When vectors are printed they are surrounded by “[ ]” and a list is surrounded by “( )”. However, a list and a seq are both surrounded by “( )”.

2009-01-13
110ERROR

This may be nit picking, but the “non-svn?” function doesn’t work very well unless you are checking stuff in the current directory. For instance….

user=> (defn non-svn2? [file] (not (.startsWith (.toString file) “.svn”)))
user=> (non-svn2? (File. “/Users/kaz/install/clojure/.svn”))
true

You could just make note of it if you don’t want to do it the right way, which is more complicated. That way someone doesn’t copy it into something of value.

I wrote the program as specified without looking at your example. I came up with something a bit more safe. But I didn’t use the “for” construct as requested.

(defn non-blank? [line]
(not (= 0 (count (seq line)))))

(defn regex-matches? [str regex]
(not (nil? (re-find regex str))))

(defn non-svn-dir? [file]
(not (and (.isDirectory file)
\t (regex-matches? (.getName file) #“^\\.svn$”))))

(defn non-svn? [file]
(loop [file file]
(and (non-svn-dir? file)
\t (let [parent (.getParentFile file)]
\t (or (nil? parent)
\t (recur parent))))))

(defn clojure-source? [file]
(and (.isFile file)
(regex-matches? (.getName file) #“\\.clj$”)))

(defn count-file [file]
(with-open [rdr (reader file)]
(count (filter non-blank? (line-seq rdr)))))

(defn clojure-loc? [dir]
(reduce + (map count-file
\t(filter clojure-source? (filter non-svn? (file-seq dir))))))

2009-02-16
128SUGGEST

I recommend.. “Notice that the update function is conj (short for conjoin), not cons. The important distinction here is argument order because of the way commute is defined.

On a first pass read about commute, I misunderstood what you were specifically going on about with argument order and it took a few moments to get back on track.

You say, “Using commute instead of ref-set makes the code … less contentious for threads. … The important distinction here is argument order.”

I took this to initially mean that the argument order matters because it is less contentious for threads and not because of the way commute works/is defined.

I know you get on about this right after you show

(cons item sequence)
(conj sequence item)

But I was on the wrong track and thought you were merely just stating it as a matter of fact and not that it is the reason to care about ordering.

2009-01-13
12TYPO

Missing “will”: Starting from scratch, you [will] build a usable subset of Lancet by the end of the book.

2009-01-13
88ERROR

add

(use ’[clojure.contrib.except :only (throw-if)])

before defining safe-instantiate-task.

2009-01-13
133ERROR

@counter
-> 1

it should be

@counter
-> 0

We redefined the counter back to zero. Then we unsuccessfully tried updating it. And then when we print it. Thus the value is 0 and not 1.

2009-01-13
41ERROR

You define apply as:
(apply f argseq)

but the Clojure API defines it as:
(apply f args* argseq)

This caused a bit of confusion for me on pg 135 in your definition of parallel-guess-pi. Apply was being passed 3 arguments instead of the expected 2.

2009-01-29
135SUGGEST

I recommend taking the time and space to explain exactly how the parallel-guess-pi works.

My take on this…(Feel free to use or change.)

Line 2 defines trials as the number of guesses that each agent should take. We don’t care if we are slightly off and do not mess with the remainder since this is a case of shot guns and grenades only needing to be close enough.

Line 3 defines defines agents as a list of individual agents. This list is created by a for comprehension. We just need to loop as many times as there are threads and we do not need any information from the looping process. Thus we use “_” to throw away the number generated by using “range”.

Line 4 sends each agent on its mission. Remember that doseq executes “(send a run-simulation)” in series, but send returns immediately.

Line 5 waits for all of the agents to finish. It is interesting to note that apply is being used where we do not see any parenthacies around the second argument. This works because “agents” is a list.

Line 6 merges all the results together into a single number. You might note that this is the first time that we are using “apply” to pass in an argument besides the contents of the argument list. We are passing in “+” to the “merge-with” function in addition to the maps. Thus there are three arguments instead of two.


Also it is minor, but if someone is typing in everything this name conflicts with the one already defined. Maybe changed it to parallel-reckon-pi.

2009-01-29
137OK

I think it would be very helpful to others to add

(let [foo “let foo”] (println foo))

as an example and explain the difference between it and

(let [foo “let foo”] (print-foo))

and

(binding [foo “bound foo”] (print-foo))

I have used thread local variables before in Java, but I think it would be helpful to see how and why this would be used. Off hand it would seem a lot less likely to be needed because of the functional nature of the language.

I also think the example you used could be slightly expanded, to clarify what exactly is overshadowed. Below is what I had in mind.

(def foo 10)
(def bar 10)
(defn print-foo2 [] (let [bar 3] (println foo bar)))
(print-foo2)
(binding [foo “bound foo” bar “bound bar”] (print-foo2))

2009-02-16
139ERROR

for other readers of B4…

(defn memoize
[function]
(let [cache (ref {})]
(fn [& args]
(or (@cache args)
(let [result (apply function args)]
(dosync
(commute cache assoc args result))
result)))))

is the missing function definition. It was removed from contrib and I don’t readily see it in my version of clojure. It is suppose to making its way there?

2009-01-13
20OK

In the “introduction” example, the variable “past-visitor” is better named “past-visitor?”. That’s in my opinion of course. Forgive me for speaking out of turn!

2009-01-13
142SUGGEST

I think the description of how the function works could be made a bit more clear.

Line 2 stores the original value of the agent in sentinel. This value sticks around with the function because it is referenced by the function. Whenever you see the sentinel value equal to the agent value, you know that the function has not yet run. Also remember that “[& args]” means that the args symbol is assigned the one or more arguments passed to the anonymous function.

Inside the new function…. must use send-off instead of send. **Remember that send-off sets the agent to the return value of the function that ran. Thus when you try to run it again, the value of the agent and the sentinel will not match and the function will not be executed twice.

2009-01-13
26OK

The Clojure implementation of blank? has “Download examples/introduction.clj” above it. Wrong file. As an aside, I don’t know what common practice is, but it might be nice for beginners if a predicate function like blank? returned true or false.

2009-01-13
26OK

SORRY — INCORRECT ERRATUM ABOVE. “The Closure implementation of blank? …” It is not the wrong file at all. Forgive my hastiness. I thought it was a separate file for each code example, but it’s not.

2009-01-13
176ERROR

(define-ant-task mkdir mkdir)

is the correction… as someone pointed out there is a second argument missing, but it is not a string.

2009-01-13
176SUGGEST

nitpicking, but what about

(sort (task-names))

instead of

(task-names)

No one would really look through an unsorted list that long. :)

2009-01-13
40TYPO

In the example code involving small-int and big-int, the return values are inconsistent: #’small-int vs #’my-app/big-int. When I run that code in the REPL, I get #’user/small-int and #’user/big-int.

2009-01-13
41TYPO

The two code examples involving interleave contain incorrect formatting, either for the code, or the return value. (“Attack at midnight” is some extra context if needed.)

2009-01-13
43SUGGEST

On this page, you introduce the reader to the functions true?, false? and nil?. It might be worth explaining the convention of using question marks in the names of functions that behave as predicates. Question marks in function names will be seen as unusual by a subset of your readers.

2009-01-29
53TYPO

Inconsistent formatting of exceptions (e.g. java.lang.Exception: No such namespace: File). In one case it’s preceded by an arrow and is blue. In the other case it’s not preceded by an arrow and is the same colour as the code.

2009-03-20
183OK

(defmulti my-print class)
(defmethod my-print String [s]
(.write out s))

You define both of these methods and then turn around and ask us to call
(my-println “stu”)

This works because we did define that function earlier, but obviously it should be

(my-print “stu”)

because we are testing what we just wrote.

2009-01-13
184OK

“… each implementation of my-println …” should be
“… each implementation of my-print …” We defined “my-print” as a mutli-method and not “my-println”.

2009-01-13
184OK

(my-println 42)
42

should be

(my-print 42)
42

2009-01-13
185OK

(my-println (java.sql.Date. 0))

should be

(my-print (java.sql.Date. 0))

and

(my-println (java.util.Random.))

should be

(my-print (java.util.Random.))

2009-01-13
187SUGGEST

You indicate to place the code in the namespace “exam-
ples.multimethods.account” But you show the results of typing stuff at the REPL as still being in the user namespace. It is possible this could be confusing to someone since they are likely to type “(ns examples.multimethods.account)” at the REPL prompt.

For brevity in the book, you could just note that you are not issuing that command at the REPL.

2009-01-13
63OK

“So it turns out that writing indexForAny without for loops or variables is actually easier. As a bonus, the code becomes shorter, easier to read, and less error prone.”

I found the Clojure version of indexForAny to be hard to read, hard to understand (it needed a lot of explanation in the book) and quite error-prone (I made a lot of errors trying to reimplement it without looking at the source).

The LOC metric is not fair, because the Clojure code is very dense. This would be more fair:

(defn indexed [string] (map vector (iterate inc 0) string))
(defn index-of-any [string chars]
  (let [index-of-matching-char
         (fn [[idx char]]
           (if (get chars char) idx nil))]
    (some index-of-matching-char (indexed string))))

Also worth mentioning is that I found it so much easier to implement using loop/recur. It worked first time, in fact.

(defn index-of-any [string chars]
  "Returns the first index in the string where any of the given characters occurs."
  (loop [current-idx 0 s string]
    (let [current-char (first s)]
      (if (contains? chars current-char)
        current-idx
        (recur (inc current-idx) (rest s))))))

That’s clearer to me because the code is highlighting the essential point of the function: if the current character is a “matching” one, return the current index.

There might be some value in presenting both functions.

Just for your consideration :) By the way, my background is Ruby, not Lisp.

2009-02-16
79TYPO

“In this case none are needed”: missing period at end of sentence.

2009-01-13
81OK

Wouldn’t:

(Manifest. r)

be:

(def newManifest (Manifest. r))

, since you want to be able to refer to the new object as newManifest?

2009-01-13
18TYPO

The snippets don’t reflect the quotes in output that are actually there.

user=> (hello “Hans”)
“Hello, Hans”

2009-01-13
11OK

Hi Stuart,

This is an open request for you to add a chapter. I know you are probably rolling your eyes by now given that you have an aggressive publish date, but please here me out.

Object Oriented design has reigned king for well over a decade. And a large majority of your likely readers come from the Java and .NET world. Many of us, including myself, have years of experience designing utilities and applications with object oriented design in mind. This mindset has a way of shaping the larger structure of programs—which includes design patterns and even larger super structures.

The way that you encapsulate methods and data is certainly different in Clojure. You don’t define them as private, but use “let” instead. Name spaces are not set in stone. OOP polymorphism primarily revolves around being able to call a particular method chosen at runtime and it usually involving a hierarchy of types. And this polymorphism is used everyday without much special thought given about it. But as you point out in your chapter on multimethods, it isn’t used frequently when programming in Clojure. Thread-local bindings in Clojure go much further in concept than java.lang.ThreadLocal in Java. And lisp macros open up a world of wondrous possibility and danger that you don’t find in Java and .NET (including F# from what I can tell). And java’s ideas on metadata with annotations is far less capable than Clojure’s idea of meta data.

Clojure is far more alien to us than even a language like Scala or F# that allows one to blend OOP and FP. Sure you can call Java from Clojure, but that isn’t the point. Immutability and the world of verbs is likely to have a large impact on the structure of things.

It would be nice to have some thoughts from you as it relates to Clojure about how FP affects programs on the large, the medium, and the small. For example on the small, I would have never considered storing helper functions in meta data like you did with Lancet in chapter 7. On the medium, you could talk about how the need for OOP design patterns are largely mute or the possibility of DSLs in ones code base saving a great deal of time and LOC. And on the large, I just don’t really know. For a start, you might take a look at " orgonblog.spaces.live.com/Blog/cns511.entry " for an idea of what I mean by the large, the medium, and the small. It seems like a good way to partition and discuss things.

I think it is even more important to air out this idea of structure because there seems little if anything written about the subject. You can find plenty of material about OOP analysis and design, but what is there for FP analysis and design? I can find very little on the subject, especially languages like Clojure that are more purely functional. I’m sure one could write an entire book on this subject, but it would be great to hear 15-20 pages from someone that has written a substantial number of lines of code in a FP language on a nontrivial sized code base.

Feel free to email me at kaz0358 AT ksu.edu if you have any thoughts or questions on what I’m trying to get on about.

2009-01-29
90TYPO

’seek-a-bull: missing/incorrect quotes? Should be “seek-a-bull” or “seek-able” or “seekable”? (Quotes included.)

2009-01-13
157TYPO

“Rewrite chain using spicing unquote to splice in rest:” — “splicing” is typo’d as “spicing”.

2009-01-13
42ERROR

Keyword :tag, :doc Section 2.1, Forms, on page 37
The line above, from Figure 2.1, claims that keywords are covered in Section 2.1 on page 37. There is no mention of keywords on page 37 or anywhere else in Section 2.1 except in the quoted line.

2009-01-13
108TYPO

Formatting error for the output of the function after the comment “;overkill”. The output should be blue and left-aligned.

2009-05-27
109TYPO

Incorrect indentation in code under “filter to count only non-blank lines”.

2009-01-13
148TYPO

“The Only Way To Encapsulate A Pattern.” I think you have two periods at the end of that sentence.

2009-01-13
153TYPO

In “when-not” at the bottom of the page, “-not” is a different colour from “when”.

2009-05-27
155TYPO

“In the code above, the bolded portions are the body to be generated, and the remainder is the macro code.” Maybe it’s too late at night, but I’m not seeing any bolded portions.

2009-04-29
66SUGGEST

You should give a quick example to on how to call a java method that takes a variable number of argument list. I ran into this and it took a bit of googling to find the answer.

This seems like a good example…

(. System/out printf “%s %s %s” (to-array [“1” “2” “\
\
”]))

2009-02-16
63TYPO

word “language” spelled incorrectly in footer

2009-01-13
131TYPO

“That is because senddoes not know the new value.” Note the missing space.

2009-01-13
137OK

I did not find the section on bindings very illuminating. Turning to the REPL, I found the following sequence helpful.

user=> (def foo 10)
user=> foo
user=> (binding [foo 42] foo)
user=> (let [foo 42] foo)
user=> (defn print-foo [] (println foo))
user=> (print-foo)
user=> (binding [foo 42] (print-foo))
user=> (let [foo 42] (print-foo))
user=> (print-foo)

Hope that’s of use.

2009-02-16
142TYPO

“Line2 stores the original value…”. Note the missing space.

2009-01-13
145TYPO

“runonce returns a vector of the three locally-defined functions has-run, reset-fn, and the anonymous function starting on line 9.” “has-run” is supposed to have a question mark after it.

2009-01-13
44TYPO

An overview of the different usages of the # would be extremely helpful. There are at least 7 in the book :
#{} - Sets
#“” - Regex patterns
#^ - Metadata
#’ - Var-quote
#() - Anonymous function literal
#<> - as in #<file:/Users/stuart/repos/clojure/clojure.jar> on p.68
#= - as in #=(var user/project) on p. 85
Only the first four are explained in the book.

2009-01-29
59ERROR

(def persistent-stu #^{:com.thinkrelevance/persistent true} stu)

should something like:

(def persistent-stu #^{:com.thinkrelevance/persistent true} {:name “stu”})

because of the same “where did the meta go?” warning you described on the previous page.

2009-01-13
121ERROR

… function lets you pass keys as strings instead of keywords

2009-01-13
70SUGGEST

(make-array (. Character TYPE) 5)

I think you should really show how to make a native array type. I spent quite a bit of time searching for this. You might also mention that there are helper functions for creating native number arrays such as int with the int-array function.

2009-02-16
23ERROR

The line continuation character in the Windows command line interpreter is carat (^) not backslash (\\). The launch command on Windows should be:

SET CLASSPATH=/jars/clojure.jar;^
/jars/clojure-contrib.jar;^
/path/to/book/code
java clojure.lang.Repl

2009-01-13
57SUGGEST

I think it would be very helpful to talk about the three following cases as it relates to loop/recur. And what their impact is particularly on memory. Some might be surprised that it does not have tail call optimization.

This is the simplest case that I could think of. Maybe this is the right spot to talk about it. I don’t know what you got cooking in chapter 5.

(defn piano-plus [x y]
(if (= x 0)
y
(inc (piano-plus (dec x) y))))
;create stack overflow
(piano-plus-alt 200000 5)

(defn piano-plus-try [x y]
(if (= x 0)
y
(piano-plus-try (dec x) (inc y))))
;still fails
(piano-plus-try 2000000 5)

(defn piano-plus-better [x y]
(loop [x x y y]
(if (= x 0)
y
(recur (dec x) (inc y)))))
(piano-plus-better 2000000 5)

2009-02-16
123SUGGEST

Here is a simple idea for chapter 5. There should be at least a couple examples of how to get around the “recur only at tail position” problem.

(defn fib [n]
(if (< n 2)
n
(loop [x 0 y 1 place 2]
(if (= place n)
\t(+ x y)
\t(recur y (+ x y) (inc place))))))
(fib 100)

(defn fib-list []
(loop [x 0 y 1 ]
(let [next (+ x y)]
(lazy-cons y (list (recur y next))))))
; doesn’t work due to limitation in clojure — can only recur at tail position

; have to be creative
(defn fib-list []
(defn f [[x y]]
(list y (+ x y)))
(map first (iterate f ’(0 1))))
(take 20 (fib-list))

2009-01-29
40ERROR

The class of expression

(def big-int (* 1000 1000 1000 1000 1000 1000))

as received from evaluating expression
(class big-int)

is java.lang.Long on my computer instead of java.math.BigInteger in the text.

I’m using Max OS X 10.5.6 with this version of JVM: Java™ 2 Runtime Environment, Standard Edition (build 1.5.0_16-b06-284)
Java HotSpot™ Client VM (build 1.5.0_16-133, mixed mode, sharing)

2009-01-13
33TYPO

bad hyphenation for “stand-alone” in the text it’s hyphenated “stan-dalone”

2009-03-20
34TYPO

in the ant example xml the “compile” target is colored differently than the rest of the example (blue instead of red), this was in Evince 2.24.1 (Ubuntu 8.1)

2009-05-27
50TYPO

“(let [bindings] exprs)” isn’t formatted the same way other representations like this are, that is, with blue text and a grey box around them.

2009-01-13
56TYPO

(loop [bindings ] exprs) is missing it’s gray background box

2009-01-13
57TYPO

“(recur exprs*)” is missing it’s gray background box

2009-01-13
60TYPO

the result of “(meta #’meta)” is different than shown (using the version of clojure asked for in the first chapter). The result I get from the REPL is as follows:
myapp=> (meta #’meta)
{:line 147, :name meta, :file “core.clj”, :arglists ([obj]), :doc “Returns the metadata of obj, returns nil if there is no metadata.”, :ns #<Namespace clojure.core>}

2009-01-29
60ERROR

(def persistent-stu #^{:com.thinkrelevance/persistent true} stu)
-> #’user/persistent-stu

This doesn’t add the metadata, in fact it’s the same form as your “be careful” example on the previous page.

2009-01-13
154TYPO

(when-not false (println “this”) (println “and also this”))
this
and also this

2009-01-13
135ERROR

Executing this code from page 135
(def counter (agent 0 #(throw-if (not (number? %)) “not a number”)))

triggers the following exception:

java.lang.IllegalArgumentException: No value supplied for key: user$fn__1197@89f302 (NO_SOURCE_FILE:2)
[Thrown class clojure.lang.Compiler$CompilerException]

I’m using the latest versions of clojure (r1207) and clojure-contrib (r368)

2009-01-29
73ERROR

(. currentTimeMillis System) does not work.
Shouldn’t that be
(. System currentTimeMillis)
?

2009-01-29
216TYPO

first line should read

(compojure.jetty/start snippet-server)

2009-01-29
207TYPO

“You can add your own assert expressions to test-is, see the source code for the assert-expr multimethod for details.” The comma in that sentence should be a semi-colon.

2009-01-29
209SUGGEST

In the function ’insert-snippets, I was confused for a while at the use of the let variable “created_at”. That is the name of a column, but you are using it to represent a timestamp. A small thing, I know, but I think the variable would be better named “now” or “timestamp”.

2009-02-16
211TYPO

“Verify that the improved select-snippets works: […]”

The code following this sentence doesn’t call select-snippets. I’m pretty sure it doesn’t even call it directly. I may be wrong, but at the very least, I’m confused, which is a worthwhile bug report :)

2009-01-29
213TYPO

Sorry if this erratum report is incorrect, but… “This example is from demo.cll:” I’m going out on a limb and assuming that should be “demo.clj”.

2009-01-29
217TYPO

“submit-button is a trivial helper, with a signature analogous [to] text-area’s.” Missing the “to”.

2009-01-29
225SUGGEST

It’s not particular to this page, but on one page we have three code extracts with the exact same header: “Download examples/snake.clj”. This repetition seems unnecessary to me; you may want to save some space. As a reader, I’m well aware that I can download the sample code, and am willing to look at the beginning of the chapter to find out how to do so. Just a thought.

2009-01-29
226OK

(defn head-overlaps-body? [{[head & body] :body}]
(includes? head body))

You have head and body flipped around as arguments to “includes?”.

(defn includes?
“Returns true if s contains something equal (with =) to x.”
[x s]
(if (some (fn [y] (= y x)) s)
true false))

2009-01-29
227OK

(turn (create-snake) [0 –1])

I wonder why you aren’t using (dir VK_UP) instead of [0 –1]. That goes for a few other function calls up to this point as well.

2009-02-16
54SUGGEST

You don’t cover all of the special destructuring forms. I recommend that you either add them. Or point out that there are others and create a footnote with the URL to clojure.org/special_forms indicating to look under let.

I was a bit surprised by your final exmaple, the snake game.

2009-02-16
19TYPO

after the svn co of clojure-contrib you say “cd clojure” when you need to “cd clojure-contrib” to build clojure-contrib

2009-01-29
18SUGGEST

It would help to know which svn version of clojure and clojure-contrib were built and bundled with the sample code in the text. That way, if I’m used to building it I can just build the correct versions to following along with the text.

2009-01-29
230TYPO

“paint-Component calls proxy-super to invoked the normal JPanel behavior, …”. Should be [invoke], not [invoked].

2009-01-29
230TYPO

“If the game is over, it displays a dialog and then resets the game.” The code indicates that these actions are performed in the opposite order. I think the order you mention in the text is more sensible, so perhaps the code should change rather than the text.

2009-01-29
223TYPO

Dimension doesn’t appear in the Java imports, but it is used in ’game-panel.

2009-01-29
22TYPO

The line (dosync & exprs) should be in a different colour/background colour, like (commute r update-fn & args…)

2009-01-29
46TYPO

For example, you can create a person struct:
should be
For example, you can create a book struct:

2009-01-29
228SUGGEST

Just reporting a version-skew: the discussion of the Snake mutable model mentions “atoms”, which are not yet described in the section on concurrency (or anywhere else).

2009-01-29
22ERROR

I question whether the hello function is truly safe for multiple threads, since the deref outside of the transaction could get a different snapshot than the commute inside the transaction.

2009-01-29
26ERROR

Sample code shows correct full namespace specification for fibs, but prose says “you must say introduction/fibs”.

2009-01-29
31TYPO

Did you mean to leave out the verb in “The whole language there, all the time”?

2009-01-29
32SUGGEST

It’s not clear that the first cond example is Common Lisp and will actually throw an error in Clojure.

2009-01-29
210OK

I would strongly recommend changing the example to something other than a database of clojure code snippets. Having clojure code contained in string literals inside code examples inline in a book is very messy, and given that this is a fairly introductory book on the language, it’s not reasonable to assume all readers will be able to quickly differentiate the snippet clojure code from the actual clojure code. How about using the same music example from earlier in the book? If not that, then just about anything else would be better than source code.

2009-02-16
210OK

“(let [created_at (now)]…” and the accompanying explanatory sentence “The let calls now only once, guaranteeing that both records have the exact same created_at value” distract from the point of this chapter, not to mention introducing the confusion of “created_at” being the name of two different things. It’s not important that the two rows have the same created_at value.

2009-02-16
134ERROR

I got different output for the validated message output than that in the text. I’m using clojure svn revision 1195 and clojure-contrib revision 335 which I believe matches the expected builds for b5 of the book. Here was my output:
user=> (add-message “not a valid message”)
java.lang.IllegalStateException: Not a valid message (NO_SOURCE_FILE:0)
user=> @messages
()
user=> (.. *e getCause getCause)
nil
user=> (.. *e getCause)
#<IllegalStateException java.lang.IllegalStateException: Not a valid message>

2009-01-29
135ERROR

The following also occurs on clojure r1195 and clojure-contrib r335
user=> (def counter (agent 0 #(throw-if (not (number? %)) “not a number”)))
java.lang.IllegalArgumentException: No value supplied for key: user$fn__544@11ef443 (NO_SOURCE_FILE:206)

2009-01-29
135ERROR

agent validators changed at r1193 after that use something like:

user=> (def counter (agent 0 :validator #(if (not (number? %)) (throw (IllegalStateException. “not a number”)) true)))
#’user/counter

2009-01-29
46ERROR

“Internally, the struct stores its values in indexed slots, which results in slightly faster access than a map.” Is this true? The Clojure web site talks about more efficient key storage but doesn’t mention faster access. The implementation of PersistentStructMap shows each lookup must still pull the key out of a map. It’s a shared map of keys, thus the efficient storage. The accessor function is there if you really need faster lookup.

2009-01-29
54TYPO

“The greet-author-1 functions works fine” functions should be function?

2009-01-29
23TYPO

The doc output for str needs to be updated to reflect the new namespace. The page shows clojure/str but the latest clojure code shows clojure.core/str.

2009-01-29
25SUGGEST

The use of the repl.sh script appears only on page 25, but I did all examples before that starting the REPL as described on page 18. May be worth mentioning how to start the REPL with history support thanks to the script repl.sh on page 18.

2009-01-29
31TYPO

“These features makes”, should be “make”

2009-01-29
42OK

What about accentuated characters?

user => (apply(interleave “Bonjour raphaël” “Ceci est le code secret”))

“BCoencjio uers tr alpeh ac�old”

2009-01-29
239TYPO

“While writing the book, I used Emacs …”
“I used Emacs while writing the book.”
The second sentence is redundant.

2009-01-29
210ERROR

As clojure.contrib.sql has changed, the current print-snippets function does not work anymore. The correct form is now:

(defn print-snippets []
(with-query-results res [“select * from snippets”]
\t\t(println res)))

2009-01-29
53SUGGEST

In the explanation of
(let [bindings] exprs)
there’s no word about exprs*. Might be worth mentioning that these are the expressions for which the bindings apply.

2009-01-29
59SUGGEST

about “plan to use do rarely”:

I’m new to clojure and lisp style programming. In other programming languages, I rarely have an if or an else branch with only one statement. Is this different in clojure? Maybe an example for people new to this style of programming?

2009-02-16
62TYPO

Here is the metadata for the str var:

should be
“… the str function:”

2009-01-29
53SUGGEST

I think it would be helpful to show what you mean by “def and defn work by creating Vars and root bindings …” I certainly didn’t give this much thought initially.

A simple example would be….

(defn foo []
(defn bar [] “bar”)
nil)

(bar)
—-> problem

(foo)
—-> nil

(bar)
—-> “bar”

And then generally indicate people should not do this. And this is what “let” and anonymous functions are for.

2009-02-16
66SUGGEST

index-of-any will work with Vector or String search chars if you use (set) within the get:

user=> (defn indexed [s] (map vector (iterate inc 0) s))
#’user/indexed
user=> (defn index-of-any [s chars]
(some (fn [[idx char]] (if (get (set chars) char) idx))
(indexed s)))
#’user/index-of-any
user=> (index-of-any “zzabyycdxx” #{\\b \\y})
3
user=> (index-of-any “zzabyycdxx” [\\b \\y])
3
user=> (index-of-any “zzabyycdxx” “by”)
3

2009-01-29
88ERROR

The version of repl.sh that I puleed from github did not have ant.jar and ant-launcher.jar in the classpath.

2009-01-29
148SUGGEST

The same code appears (except for namespace declaration) on 148 and 149 at the end of chapter 6. It is good to summarize all code snippets at chapter end, but in this case this appears reducdant.

2009-01-29
145TYPO

Repetition of the word “are” in the following

If the agent’s value and the sentinel are are equal, then the original function has never run.

2009-01-29
65TYPO

Misspelling “langauge” in footnote at end of Exploring Clojure extract

2009-02-16
173TYPO

“by create a deftarget” should be “by creating a deftarget”

2009-01-29
172SUGGEST

It would be nice to know exactly which files I needed to use to “catch up” on the dependencies for lancet. I closed the REPL for the first time and didn’t have the versions that I typed still in there, and had to do a bit of digging to find which ones I needed. It wasn’t much work, but just a quickie note on which files were necessary to type the following additions to lancet would’ve been brilliant. For example, I wasn’t sure if step 3 included step 1 and 2 already or if I needed all three files. Also, whether I should use the _repl.clj or the_complete.clj versions.

For anyone else, the following worked:
(use ’lancet.step-2-complete)
(use ’lancet.step-3-complete)

yep no step one, and dash ‘’ not ’’ underscore (not sure why the files are names with underscore ’’, the namespaces in there are dash ’’ and the use wants dash ‘-’. Still need to learn more about clojure libs :)

2009-02-16
180ERROR

The return value of the following is incorrectly nil:

(sleep {:seconds 5})
… time passes …
nil

Instead the return value is the task itself.

2009-01-29
179ERROR

The return value of the following is incorrectly nil:
(mkdir {:dir (java.io.File. “foo”)})
[mkdir] Created dir: /Book/code/lancet/foo
-> nil

The return value is actually the task itself which is also emphasized in the text.

2009-01-29
78SUGGEST

I’m a complete newbie in Clojure, but I’m surprised of the code for sum-to as it seemed to me to be java-like in clojure. And indeed (I think), there’s a much more elegant approach:

(reduce #(+ %1 %2) 0 (range 10))

I find it strange to have such code after having ported a java method to clojure a bit earlier, and touting the fact that it didn’t need branches or loops.
Is my code not elligible for performance optimisation? Why? Micht be interesting to know.

2009-02-16
192TYPO

“require difference balance”-> “…different…”

2009-01-29
192SUGGEST

P. 192 talks about the namespacing mechanism for keywords; and the use of keywords to designate a semantic class.

P. 45 says “keywords cannot contain a period (.) or name a class.” This does not exactly contradict p. 192, but given the different meanings of the term “class” and the use of the period in namespace definitions, some explanation would come in handy.

2009-02-16
191OK

It seems weird to show how you can use ::Checking as a shorthand for ::examples.multimethods.account/Checking and then use the longer version. I’d probably introduce the alias version first and then show the ::Checking version and use the ::Checking version in all subsequent examples.

2009-02-16
19OK

on *nix and MacOs rlwrap is a handy library that adds history, inline-editing and much more to all kind of shells so just run:
rlwrap java -jar lib/clojure.jar
It even keeps history over sessions.

2009-02-16
191ERROR

If following along at the REPL and creating the multimethod at the REPL, the following isn’t correct:
(defmulti interest-rate :tag)
(defmethod interest-rate ::Checking [_] 0M)
(defmethod interest-rate ::Savings [_] 0.05M)

The ::Checking and ::Savings need to be ::acc/Checking and ::acc/Savings.

2009-03-20
192ERROR

If following along and typing at the REPL, the following code is incorrect:

(defmulti account-level :tag)
(defmethod account-level ::Checking [acct]
(if (>= (:balance acct) 5000) ::Premium ::Basic))
(defmethod account-level ::Savings [acct]
(if (>= (:balance acct) 1000) ::Premium ::Basic))

There needs to be namespace qualifications on the tags, ::acc/Savings ::acc/Checking ::acc/Premium ::acc/Basic

I keep bringing these differences up as the previous chapters were all done at the REPL just fine (with minor differences in namespaces for some values) with the text as printed. This section is different because you explicitly called out putting the first bit of code in a seperate file.

So yes this code works as listed if it’s put in the file and :reload’d but the mixture of REPL code and code in the file make it a bit unclear.

2009-03-20
202TYPO

code listing is cut off at the bottom of the page

2009-05-27
200OK

instead of:
(def mkfoo
(instantiate-task ant-project “mkdir” {:dir “foo” }))
-> #’user/mkfoo

wouldn’t the following also test it (building off the created functions from (define-all-ant-tasks) in the last chapter):

(mkdir {:dir “foo”})

2009-02-16
202ERROR

the functions in the doto’s weren’t updated like they were in step 2 on page 126

2009-01-29
217TYPO

“functions like text-area and submit-
buttion with plain” submit-buttion is misspelled. It should be submit-button

2009-01-29
44TYPO

At the end of the page, “or nil if they key is not found” should be “or nil if the key is not found”: s/they/the/.

2009-01-29
46TYPO

The code examples in Table 2.1 is not properly typeset as code. Code in Table 2.2 on the next page is slightly better, but inconsistent with the font used for code in the main text.

2009-02-16
14TYPO

In the middle of the page, some of the examples of the form grammar are incorrectly typeset:

(example-fn zero-or-more-arg*)
(example-fn one-or-more-arg+)

In both cases, the “or” part of the argument is in brown instead of the blue used for the rest of the sample.

2009-03-20
52SUGGEST

The intro to section 2.3 correctly states that “a function call is simply a list whose first element resolves
to a function”, but there is no example of computed function calls à la Scheme, where the car of the invocation form is not just a symbol.

The introduction of make-greeter could be the opportunity to present this feature:

((make-greeter “Hello”) "Stuart)

2009-01-29
52OK

Given that we have seen how to give a name to an “anonymous” function (and that Clojure is a Lisp-1), this begs the question: what is the difference, if any, between those:

(def hello-1 (fn [name] (println (str “Hello, ” name))))
(defn hello-2 [name] (println (str “Hello, ” name)))

Is defn just a shortcut?

2009-01-29
21OK

(ref initial-state)
-> (ref ’initial-state)

2009-01-29
216TYPO

“Compojure converts the options map that into attributes on” shouldn’t have the “that” and thus say “Compojure converts the options map into attributes on”

2009-01-29
15OK

Current version clojure from current sample zip file fails on Win XP with “Exception in thread ‘main’ java.lang.NoClassDefFoundError: jline/ConsoleRunner”

zip file downloaded 1/30 0945 Eastern from PragProg website.

2009-02-16
159TYPO

Paragraph just before “Shortcutting…with memoization”:
Laziness is a powerful ally.

2009-02-16
139TYPO

Incorrect link in the footnote on page 139

2009-02-16
172TYPO

After an example is the following text:
swap! returns the new value. As usual,
the _ indicates an argument that
you don’t care about.

The “As usual, … care about.” seems to make no sense here, because there is no use of underscore nearby. There is an example that uses underscore for an ignored argument in the section “Validating Agents and Handling Errors”:
(send counter (fn [_] “boo”))

2009-02-16
172TYPO

Describing when compare-and-set! can be used, the third bullet point reads, “The value stored in the atom can use identity comparison, nor equality. (Generally this will imply a primitive.)”

This is not clear (and may be wrong): perhaps either

  • “The value…can use identity comparison or equality” or
  • “The value…uses identity comparison _rather
    than_ equality…”

It’s hard to tell if this is intended to be “or” or “not”: “not” could be better expressed as above, and “or” should not have the comma.

2009-02-16
147TYPO

CONTEXT
You are given a sequence of coin toss results, where heads is :t and tails is :h:
[:h :t :t :h :h :h]
How many times in the sequence does heads come up twice in a row?
In the example above, the answer is two. Toss 3 and 4 are both heads, and toss 4 and 5 are both heads.

COMMENT
Heads should be :h and tails should be :t.

2009-02-16
148TYPO

CONTEXT
Write a function named by-pairs that does performs this transformation.

COMMENT
Either “does” or “performs”.

2009-02-16
149TYPO

comp is used to compose compose two or more functions:

2009-02-16
152SUGGEST

The box is “underlapping” the footer; it’s not clear if there’s hidden text.

2009-05-27
161TYPO

Clojure gives you have access to a wide variety of techniques including self recursion with recur, mutual recursion with trampoline, lazy sequences, and memoization.

2009-02-16
145TYPO

The printer used by the REPL will, by default, print the entirely of a collection.

2009-02-16
147TYPO

head is :t and tail is :h rather than head is :h and tail is :t

2009-02-16
159SUGGEST

There’s a use of multi-methods and it has not been explained earlier what it is.

2009-02-16
158OK

I don’t see why the lazyness solves the recursion problem. Will read it again, but might be explained more clearly maybe?

2009-02-16
17OK

Please could you say “Unix” and not “*nix” everywhere? The joke wears thin after the second repetition, and the regexp does not match in any useful way. You could say something in the intro to the effect that Unix includes Linux, BSD and so on.

2009-02-16
168SUGGEST

Explain this:

how commes that I can’t call add-message as this:

add-message(“bonjour”)
#<user$add_message_462 user$add_message_462@1b4a74b>
java.lang.ClassCastException: java.lang.String cannot be cast to clojure.lang.IFn (NO_SOURCE_FILE:0)

add-message( :bonjour )
#<user$add_message_462 user$add_message_462@1b4a74b>
java.lang.IllegalArgumentException: Wrong number of args passed to keyword: :bonjour (NO_SOURCE_FILE:0)

2009-02-16
42ERROR

“As the code above shows, doubles can lose precision, but BigDecimals cannot.”
I think this will confuse people who don’t know about the difference between rational and irrational numbers. It also gives a misleading view of floating point arithmetic.
user=> (Math/sqrt 2.0)
1.4142135623730951
user=> (Math/sqrt 2.0M)
1.4142135623730951
user=> (Math/sqrt 1M)
1.0
So an irrational function of a BigDecimal gets converted (of course!) to a double. Even if the answer is expressible as a BigDecimal, we still get a double. I suppose some kind of lazy object might be produced, but I doubt it.

In general it is wrong to suggest that non-trivial calculations can be done without loss of precision. The area of floating point calculations is a difficult one that does not lend itself to quick summary sentences. Maybe safest to say something along the lines of “if you think additional bits of accuracy may help, you can use big doubles, but be aware that they will probably not solve your problem and will cause your program to run many orders of magnitude more slowly”.

2009-02-16
141TYPO

Third paragraph, first line: “show-stoppper” should only have two ’p’s in “stoppper”.

2009-02-16
144TYPO

In a couple places — starting on this page, but also in following pages — you talk about “a trillion” numbers. I think you’re short a few orders of magnitude in the code (showing billions instead of trillions):

e.g. (def lots-o-fibs (take 1000000000 (lazy-cons-fibo)))

1 billion (10^9)
1000000000

1 trillion (10^12)
1000000000000

2009-02-16
153TYPO

A mutual recursion occurs when the recursion bounces between two ore more functions…

2009-02-16
154TYPO

For example, you can think of oddness and evenness in terms
of a single concept: parity Define a parity …

2009-02-16
155TYPO

If the return value is a function, then the trampoline assume (should be: ‘assumes’) …

2009-02-16
157TYPO

replace takes an s-list, and (guess that should be an article) oldsym, and a newsym, and replaces …

2009-02-16
171TYPO

This sentence doesn’t make sense as written: “If you player set to random play, maybe you listen to several tracks in a
row by the same composer.”

2009-02-16
26TYPO

“Because Clojure objects are Java objects, you can also show any Clojure to see its underlying Java API.”

should read something like

“Because Clojure objects are Java objects, you can also show any Clojure object to see its underlying Java API.”

2009-02-16
65SUGGEST

You say “and see that Clojure enforces the :type tag”, when :type has not been mentioned before, although :tag has several times. If :type was intended with the colon, could you explain it a bit more?

2009-02-16
91TYPO

Last paragraph : Where interop is concenred … should be When interop is concerned ..

2009-02-16
140TYPO

“Line 6 defines an anonymous function”… not really, you gave it a name: fib.

2009-02-16
192OK

In the step_3_repl.clj code snippet, maybe (function) should be (apply function args)

2009-02-16
22SUGGEST

The sentence “Sets are functions of their keys” comes across a bit odd. Sets don’t have keys but rather elements. Maybe instead something along the lines of “A Clojure set can itself serve as a function, returning its argument when that is a member of the set or nil otherwise.”

2009-02-16
23ERROR

My previous erratum report on this issue seems to have been rejected due to my inclusion of a URI (Your system only warns after submission that they aren’t allowed…).

The REFER form at the bottom of this page fails due to a conflict with the user-defined HELLO function:
user=> (refer ’examples.introduction)
(refer ’examples.introduction)
java.lang.IllegalStateException: hello already refers to: #’user/hello in namespace: user (NO_SOURCE_FILE:0)

(The other errata listed for this page (books.pragprog.com/titles/shcloj/errata/add?pdf_page=23) seem to be out of sync with the current PDF version. They refer to content on other pages.)

2009-02-16
187TYPO

In the first sentence, “use input”, should be “user input”.

2009-02-16
267ERROR

Section 9.4 (Farewell) mentions GUI development as does the first list on page 248 at the beginning of chapter 9, but the chapter does not contain anything about building a GUI with clojure.

2009-02-16
31SUGGEST

I recognize that there is some disagreement here, but I believe that the consensus is that parameters are names and arguments are values associated with these names when a function is invoked. Thus, “For example, function
parameters are specified in a vector: [ ] instead of a list: ())”

2009-02-16
34TYPO

“thread safety” is inconsistently hyphenated:
STM is a higher -level approach to thread-safety than the locking mechanisms that Java provides.

This guarantees thread safety, and it is easier to use than Java
locking.

2009-02-16
91TYPO

“first time cthe class is loaded.” —> “time the class”

2009-02-16
127OK

I think it would be useful to discuss some of these functions in relation to the ones that have already been introduced. It seems strange to introduce select here with no mention of filter - a quick contrast would be helpful

2009-02-16
171TYPO

“atoms do no participate in transactions” should be “do not”

2009-02-16
171TYPO

“IF you need coordinated access” should be mixed case

2009-02-16
172SUGGEST

“This rules out swap!, which has side effects” The meaning of this is not clear to me, nor is the contrast with reset!. Above text says not to put side effects in your swap! fn (that makes sense), and now this says swap! has side effects. How is that different than “blindly updating” which reset! does?

2009-02-16
175SUGGEST

“Transactions cannot have side effects” That’s a little strong. They should not have side effects, but the language doesn’t prevent you from putting them in the transaction.

2009-02-16
192TYPO

“It is perfectly fine ot use” should be “to”

2009-02-16
192TYPO

“This function will
take an ordinary function and an agent, and retur n a new function that
will run only once, doing its work on the agent.”

There is no agent in the example.

2009-02-16
225SUGGEST

Would it be worth calling resolve instead of ns-resolve in order to see if the name is already def’ed in either clojure.core or the current namespace?

2009-02-16
164TYPO

Java Concurrency in Practive

2009-02-16
157TYPO

The result:

(replace ’((a b) (((b g r) (f r)) c (d e)) b) ’b ’a)
-> ((a a) (((a g r) (f r)) c (d e)) a))

has an extra ‘)’ at the end it should be:

(replace ’((a b) (((b g r) (f r)) c (d e)) b) ’b ’a)
-> ((a a) (((a g r) (f r)) c (d e)) a)

2009-02-16
169OK

“You cannot use commute to update the counter..”

This passage could use some clarification - I found myself rereading it several times to understand the meaning you intended

2009-02-16
160OK

I get an StackOverflowError when using the memoized versions of m and f:

user=> (time (m 250))
“Elapsed time: 356670.514612 msecs”
155
user=> (def m (memoize m))
#’user/m
user=> (def f (memoize f))
#’user/f
user=> (time (m 250))
java.lang.StackOverflowError (NO_SOURCE_FILE:0)
user=>

This is using the sample code and launch script from git.

I assume your JVM has been launched with more memory than mine, however, elsewhere in the book it’s always blown stack and memory at the same time.

2009-02-16
88ERROR

the git version of the samples didn’t have a “classes” directory so running “bin/tasklist.sh snippets/example-build.xml” couldn’t find the class. And, typing the following in the REPL “(compile ’examples.tasklist)” wouldn’t work until I added a “classes” folder.

After adding the “classes” folder and compiling the examples.tasklist code, then calling “bin/tasklist.sh snippets/example-build.xml” worked from the command line.

2009-02-16
184SUGGEST

I didn’t feel this was particularly clear: ‘The {:keys [body dir]}
make the snake’s body and dir available as their own bindings, and the
:as snake binds snake to the entire snake’

2009-02-16
172SUGGEST

I don’t see how “As usual, the _ indicates an argument that
you don’t care about.” is useful. Your example doesn’t use _ and why it might be especially applicable to atoms is not explained.

2009-02-16
43SUGGEST

The discussion of character literals includes \
ewline, \\space, \\tab. Maybe a footnote to mention \\backspace, \\formfeed, and \\return as well?

2009-02-16
43SUGGEST

After showing how to uppercase a string there is no example to uppercase a character. The reader may be surprised to discover that .toUpperCase doesn’t magically work on characters too. Instead, Character/toUpperCase is required.

2009-02-16
122ERROR

The end of section 4.4 mentions that the lazy XML and zip-xml support will be described in Chapter 9, but Chapter 9 doesn’t seem to mention these at all.

2009-02-16
171TYPO

For example, you can set current-track to “Credo”:
(reset! current-track “credo”)
“credo”


Make the case consistent on Credo

2009-02-16
46SUGGEST

It is never explicitly stated that keyword names start with a colon character. This fact is presumably clear from the examples, but it should be mentioned directly.

2009-02-16
48OK

Section 2.2 states:
Many reader macros are abbreviations of longer list for ms, and are
used to reduce clutter. You have already seen one of these. The quote
character ’ prevents evaluation:

While it is true that ’ was used in the introduction to require/refer/use it was never explicitly discussed. Perhaps the reader happily copied the examples given. But it is likely that the reader has forgotten about any exposure to ’ by this point in the book.

2009-02-16
48SUGGEST

The Regex Pattern example in Figure 2.2 really looks as though it uses single-quotes, which is of course a syntax error. The examples in section 4.4 have a much clearer font. But it doesn’t appear that the required syntax is explicitly described anywhere.

2009-03-27
48OK

The discussion of Regex Pattern syntax should also distinguish between Clojure syntax and the Java strings required to designate Java Patterns, e.g., the awkwardness of “\\\\W+” in Java vs. the more streamlined #“\\W+” in Clojure.

2009-02-16
31TYPO

Closing paren with no matching opening paren.

In the first bullet under the ‘Lisp, with Fewer Parentheses’ section, the sentence, “For example, function
arguments are specified in a vector: [ ] instead of a list: ())” should probably be “(For example, function
arguments are specified in a vector: [ ] instead of a list: ())”

2009-02-16
40TYPO

The sentence, “Create a vector of the numbers 1,2, and 3:” is likely missing a space between the “1,” and the “2,”.

2009-02-16
54TYPO

The word “var” is displayed in inconsistent fonts. “… the following def creates a var …” displays “var” in a sans serif font highlighting it as though it were a keyword or something. This same font is used later in reference to the “var” special form. However, other references to “var” as a noun use the regular font.

2009-02-16
54OK

Inconsistent use of the terms “bound”, “binding”. At the beginning of section 2.4 we have “The symbol user/foo refers to a var which is bound to the value 10.” Under the next subsection “Bindings” we have “Vars are bound to names, but there are other kinds of bindings as well.” The Common Lisp standard defines a binding as “an association between a name and that which the name denotes”. This is the second sense used in the book. The first sense of a “binding” between a var and its value is inconsistent.

2009-02-16
56SUGGEST

Discussion of greet-author-2 function: “…bind first-name to the first-name of the function parameter.”
Should read: “…bind first-name to the :first-name of the function argument.”

2009-02-16
57OK

The example function “ellipsize” demonstrates destructuring where one doesn’t care about other elements beyond the first 3. It would be helpful to show an example to capture all remaining elements, e.g., “[[w1 w2 w3 & others] (re-split …”. This technique is not readily discovered from the Clojure site. I found it via Google Groups. Also, maybe an example destructuring a string: [[c1 c2 c3 c4] “pung”]

2009-02-16
57TYPO

This is a silly erratum, but the sentence “The quick brown fox jumped over the lazy dog.” should contain the word “jumps” rather than “jumped”. The whole point of the sentence is to exercise every letter of the alphabet. As is, the letter “s” is missing.

2009-02-16
151TYPO

Line 4 after “Tail recursion”:
“fucntion” should be “function”

2009-02-27
163TYPO

(def add-3 ((curry ) 3) should be (def add-3 ((faux-curry) 3), for “curry” is not defined.

2009-02-27
189TYPO

“Clojure’s finaly concurrency API” should be finally.

2009-02-27
190SUGGEST

The use of “pure” in the update mechanism table is confusing because I’m not sure if you mean pure in the same way that the FP chapter talks about pure. We’re supposed to use pure functions to update Refs, but impure functions are ok for Agents.

2009-02-27
8SUGGEST

The signature is the only thing that wraps to the next page on the forward. Looks odd.

2009-03-20
199OK

The code sample (defn head-overlaps-body? [{[head & body] :body}]
(includes? body head))
— shouldn’t that be “(includes? head body)”, switching head and body?

I get this for (doc includes?):
clojure.contrib.seq-utils/includes?
([x s])
Returns true if s contains something equal (with =) to x.

I’ve got the December 16, 2008 version of seq_utils.clj. Someone let me know if there’s a new, different version.

2009-02-27
284TYPO

Godel is misspelled as Gaudel.

2009-02-27
71TYPO

Do you mean to have set literals #{} instead of map literals here?

(index-of-any {\\z \\a} “zzabyycdxx”)
-> 0
(index-of-any {\\b \\y} “zzabyycdxx”)
-> 3

2009-02-27
22SUGGEST

The bullet point that begins “Redefine private to mean” refers to redefining “private”, but the example fragment of a class definition above it doesn’t mention “private”. Perhaps there should be agreement since the current topic is “so many words in Java are prescribed by the language def”.

2009-02-27
96TYPO

Uncoordinated is spelled “Uncoodinated”

2009-02-27
167TYPO

At the bottom of the page, it should read:

(rem (trampoline trampoline-fibo 1000000) 1000)

in place of:

(rem (trampoline tail-fibo 1000000) 1000)

PS
The book is excellent and there are remarkably few typos

Paul

2009-02-27
223TYPO

The example in the last entry in Figure 7.2 doesn’t make sense to me and has a trailing ‘)’. It currently reads: same as “wrapping evaluation”).

2009-03-20
284TYPO

“Gödel” comes through in the bibliography as “GÃüdel” (upper-G, upper-A-with-tilde, lower-u with superimposed double-quotes or double-prime mark…)

2009-02-27
64TYPO

“If you do not want to use a fully-qualified classname, you can map one
ore more classnames…” “ore” -> “or”

2009-02-27
64TYPO

" If you want to use a Clojure var from
another namespace, you must use its fully qualified name…" -> “fully-qualified name”

2009-02-27
65TYPO

“The simple for m of use above causes the current namespace to refer to
all public vars in clojure.contrib.str-utils.” This hasn’t been updated from beta 6. The new version uses clojure.contrib.math for the example. :)

2009-02-27
65TYPO

Some inconsistency in coloration of REPL samples. E.g., “(round 1.2)” is blue rather than brown.

2009-05-27
65SUGGEST

Just checking—the illustration of the “ns” macro should stand “as is”? In particular, the :use clause should specifically not include clojure.contrib.math. Otherwise this would obviate the preceding example of mapping the “round” function into current namespace?

3 other related suggestions:
1. Mention the ns variable.
2. Point out that the “ns” macro creates the namespace as needed (a la “in-ns” discussed earlier).
3. Clojure.org api doc suggests that “ns” is to be preferred over individual calls to in-ns/import/use. Maybe a footnote mentioning this.

2009-02-27
66OK

Considering that Clojure is advertised as a functional language it’s a shame that it makes the same mistake with “if” as Common Lisp. An expression must evaluate to a value. This applies to “if” forms as well regardless of whether or not the test form is true. The “if” form must be prepared to produce a value even when the test is false, so the “else” clause should not be optional—it should be explicit. If the programmer wants to use “if” for side effect only in the true case, then that’s what the “when” macro is for.

Back on topic…it would be helpful to throw in an example of “if-not” (not even mentioned in the book).

Also, considering how short section 2.5 is, it would be nice to cover “cond” as well. I think people w/o prior Lisp experience might have trouble with “cond”.

2009-02-27
66OK

It seems typical in Lisp books during the discussion of “if” to point out how it could not possibly be implemented as a function, thereby highlighting the difference between special forms and functions.

2009-02-27
34TYPO

‘if’ in the ‘hello’ method description in the code sample is highlighted in red

2009-05-27
170TYPO

transactions should be singular in the following sentence fragement: “Where multiple ref
updates can be coordinated in a transactions, …”

2009-02-27
22SUGGEST

It might be fun in the “Why Lisp?” section to add a quick note explaining why there is no precedence/associativity table in the book as many programmers from other languages may expect to find. Fully-parenthesized expressions eliminate the ambiguity.

2009-02-27
69SUGGEST

In an ideal world, the title for section 2.6 would read “Where’s My for Loop?”.

2009-02-27
69SUGGEST

I realize that you are merely comparing Java and Clojure implementations of the indexOfAny() method and not out to slam the Apache code. However, it is germane to the complexity and potential for errors in the Java code to note two coding errors:
1. The outer loop repeatedly invokes the length() method of the given String. This is a loop invariant and need only be called once. The inner loop avoids this mistake by capturing the result of charAt().
2. As you mention the multiple exit points, the method resorts to a magic number –1 as a return value to represent failure. However, rather than relying on a symbolic constant, these two literal values are susceptible to getting out of sync if the method is modified later. (Yeah, I know that’s not likely in this little example, but it’s not an issue at all with the Clojure code.)

On the other hand, you don’t discuss performance here. At the very least, the Clojure version requires more memory due to the ephemeral sequence created by the “indexed” function.

2009-02-27
000OK

I suggest you mention clojure.lang.Script. The REPL is good, and can load files, but simple non-interactive execution is important too, and this option not mentioned in the book.

2009-02-27
181TYPO

“During Transation A” -> “During Transaction A”

2009-02-27
183SUGGEST

In the validate-message-list function at the bottom of the page, I think it would be better to use “when” instead of “if”. I don’t think there’s a reason to return true when validation passes.

2009-03-20
188SUGGEST

Consider changing the name of the file used for backup of chat messages to have an extension of .txt instead of .clj since .clj files typically contain Clojure source code.

2009-02-27
189TYPO

errata to a previous errata - “finaly” should be “final”, not “finally”

2009-02-27
24OK

5th para (4th li)
These features make Clojure code less list-
y than most Lisps.
—————————
list-y —> listy

2009-02-27
95OK

tasklist.clj

‘duck-streams’ should be ‘duck_streams’ with and under score

2009-02-27
100OK

(use ’[clojure.contrib.duck-streams :only (spit)])

should be:

(use ’[clojure.contrib.duck_streams :only (spit)])

with an under score instead of a dash.

2009-02-27
47TYPO

In the bullet list of things a symbol might name, the Java class “java.lang.Ransom” is included. I’m guessing that Random is the class that was intended.

2009-02-27
47TYPO

In the paragraph immediately following the bullet list, Clojure should “support” not “supprt”

2009-02-27
183SUGGEST

Let me take back my previous comment about this page. I just learned that validator functions don’t have to throw an exception. They can just return a boolean value. That means the example on this page doesn’t need to throw an exception You can get rid of the if and use every? instead of not-every?. That will be much simpler!

2009-03-20
178TYPO

you can tell that calls-slow-double is slow because is does the same work over and over again. in the third paragraph should be you can tell that calls-slow-double is slow because it does the same work over and over again

2009-02-27
178TYPO

Sixth paragraph, last sentence refers to the function calls-slow-binding, but the function should be called calls-slow-double.

2009-02-27
76SUGGEST

Figure 2.4 describes the :arglists metadata key as “list of argument names used by doc”. As I argued before, I believe this should be “parameter names”. Moreover, the value of :arglists is really a list of vectors indicating the different arities and parameter names that the function will accept.

2009-02-27
47TYPO

java.util.Ransom should be java.util.Random

2009-02-27
74SUGGEST

“Most of the metadata keys on a var are added automatically…”

Wouldn’t it be simpler (and more accurate since keys can’t be added w/o values) to say: “Most of the metadata on a var is added automatically…”

2009-03-20
74OK

The syntax box for the metadata reader macro makes it look as though there are two components: #^metadata form

Instead, maybe “#^metadata-form”?

The same holds true for Figure 2.2.

2009-02-27
74SUGGEST

Multiline REPL output is colored inconsistently.

2009-05-27
74OK

The metadata for the “shout” function contains“:tag java.lang.String”, which presumably refers to the result type? There doesn’t seem to be any indication of the expected argument type in this metadata (:arglists doesn’t preserve it). The example at the bottom of the page then demonstrates that “shout” must take a String argument. However, the metadata doesn’t inform us of this fact?

2009-02-27
75ERROR

You state that function metadata can be placed at the end of body:
(defn shout
([s] (.toUpperCase s))
{:tag String})

1. You don’t explain why you’ve switched to the alternative “defn” form. In particular, you don’t mention why this doesn’t work:
(defn shout [s] (.toUpperCase s) {:tag String})
It returns a different value than expected…
2. You don’t explain why the #^Class shortcut doesn’t work here. This is a syntax error: (defn shout ([s] (.toUpperCase s)) #^String)
3. The runtime behavior of this function is different than the previously defined “shout”. This one doesn’t directly enforce the argument type, but it fails anyway:
(shout 1) =>
java.lang.IllegalArgumentException: No matching field found: toUpperCase for class java.lang.Integer (NO_SOURCE_FILE:0)

2009-02-27
75SUGGEST

The final paragraphs of the Reader Metadata section are a nice touch—clarifying distinction between #^ and “with-meta”. Stuff that I probably wouldn’t have straightened out until a second or third reading otherwise.

2009-02-27
88OK

The function unchecked-sum-to doesn’t work due to java.lang.UnsupportedOperationException: Can only recur from tail position.

2009-02-27
54OK

The syntax box for “defn”:
(defn name doc-string? attr-map? [params*] body)
makes no allowance for #^Classname metadata (before “name”) documenting the result type. Perhaps that’s for the best at this point in the book.

However, you defer discussion of “attr-map?” until section 2.7, but there are no examples of that part of the “defn” syntax there.

2009-03-20
78TYPO

“… makes it easy to cleanup resources using the with-open idiom.”
“cleanup” -> “clean up”

2009-03-20
55OK

On your PCL blog (ch. 5) you show an interesting alternative to support optional arguments rather than defining multiple arities:
(defn foo [a b & [c d]]
(list a b c d))

2009-03-20
82OK

You mention int-array, etc. for the creation of arrays of primitives. But for whatever reason, Clojure does not provide byte-array, char-array, boolean-array. Maybe a note showing how to create these via make-array:
(make-array Byte/TYPE 1024)

2009-03-20
82OK

The syntax boxes for “make-array”, “aset”, “aget” clearly show that they support multi-dimensional arrays. Perhaps you should point out in the subsequent discussion of “to-array”, “into-array”, “amap”, “areduce” that they really only work with single-dimensional arrays, e.g., (aget (into-array [[1 2 3] [4 5 6]]) 0 0) doesn’t work. Of course, Java doesn’t really have multi-dimensional arrays anyway…

2009-03-20
84OK

In the discussion of “amap” this is a little unclear:
It will then execute expr once for each element in a, with idx bound to the index of the element.
Maybe instead:
idx will range across each element of a with the result of evaluating expr stored as the corresponding element in the result array.

“expr” must specifically call (aget a idx) to produce a value based on that array element. Indeed, we can do silly things like this otherwise:
(def a1 (into-array Double/TYPE [1.2 3.4 5.6]))
(seq (amap a1 i _ (* i 2))) =>(0.0 2.0 4.0)
In other words, “expr” need not have anything to do with the elements of the input array.

2009-03-20
85OK

It’s probably obvious (dangerous words!), but just in case it isn’t you might want to highlight how in “memfn” toUpperCase loses its initial “.”, whereas in the anonymous function version the dot is necessary:
(map (memfn toUpperCase) [“a” “short” “message”])
(map #(.toUpperCase %) [“a” “short” “message”])

2009-03-20
85SUGGEST

Pages 83 and 85 both contain discussion of Java’s String.format/Clojure’s format. Should these be consolidated? I can see an argument for keeping things as is—each section has a different emphasis. So there is duplication but perhaps not unnecessary duplication.

2009-03-20
80OK

Neither the explicit constructor nor the syntactic sugar seem to support Java generics?
(new ArrayList)
(ArrayList.)

2009-03-20
88OK

In the discussion of the function integer-sum-to you don’t mention why it is necessary to use “let” to rebind the parameter’s value as an int. Why not use metadata as in ch. 2 to declare the type of the parameter:
(defn #^int integer-sum-to [#^int n] …
Apparently this requires a class rather than a primitive type.

2009-03-20
88TYPO

“Clojure’s convenient math operators (+, , etc.) make sure their argu
ments do not over flow.”

I think you mean “results” rather than “arguments” here.

2009-03-20
88OK

In unchecked-sum-to, should you use an unchecked-add rather than “inc”?
(recur (inc i) (unchecked-add i sum))
vs.
(recur (unchecked-add i (int 1)) (unchecked-add i sum))

2009-03-20
88OK

Whoops…in my previous comment about unchecked-sum-to, simply changing (inc i) to (unchecked-add i (int 1)) actually slows it down!

This seems slightly faster than unchecked-sum-to:
(defn unchecked-sum-to-2 [n]
(let [n (int n) one (int 1)]
(loop [i (int 1) sum (int 0)]
(if (<= i n)
(recur (unchecked-add i one) (unchecked-add i sum))
sum))))

2009-03-20
92TYPO

“… event driven APIs such as Swing or some XML parsers.” =>“event-driven”

2009-03-20
98TYPO

“The tasklist class is plain old Java class, consisting of constructors,
fields, and methods.”

Missing “a”: “a plain old Java class”

2009-03-20
156TYPO

Fourth line: “correctly choosingfibo over other aproaches”, there should be a space between “choosing” and “fibo” and approaches is spelled with 2 “p”s

2009-03-20
263TYPO

index-of-any is defined twice in the code example, once without a :test and once with.

2009-03-20
94OK

At the top of the page you present 3 anonymous functions using the shortcut #(…) notation. Then in the thread example you switch back to the “fn” notation. Maybe I’m not sufficiently used to the shortcut notation yet, but this is kind of disorienting. (I’m very comfortable with the “fn” notation from using Common Lisp’s LAMBDA.)

2009-03-20
95OK

When discussing tasklist.clj is it worth pointing out the difference between calling gen-class directly vs. from within the “ns” macro (i.e., :gen-class)? Specifically, no :name clause is needed when in “ns”—it’s inferred from the namespace name.

Also, is it worth mentioning how the generated class (reader.tasklist) violates the normal Java naming convention for class names (initial capital)? Is this class not really for human consumption? Or just for use from within Clojure? Would Java programmers be confused to encounter such a class name?

2009-03-20
95SUGGEST

“Add a -main function to reader.tasklist that prints
the task-list for each command-line argument.”

Should be “prints the task list”? (i.e., regular English rather than the function name defined on the next page)

2009-03-20
77OK

It might make sense to reinforce the word “interoperability” at the start of chapter 3. As it stands, the word is only used once in the entire book. I’m sure readers can figure out what “interop” means but it is kind of slangy. This would be the spot to clarify the meaning.

2009-03-20
95SUGGEST

On this page you refer to a “state structure” identified as “state” by the :state clause in the “ns” macro. On the next page you claim: “The state method is named state because that is what we requested in :gen-class.”

Is this inconsistent? structure vs. method
(The method is called “state” the structure is an atom?)

2009-03-20
175OK

great book! on the memoization using lazy seq:

def m-seq (map m …

I assume that this m is different than the m on p 173, and the m on p 174, but not clear. I.e. something like

(defn m [n]
(if (zero? n)
0
(- n (nth f-seq (nth m-seq (dec n))))))

2009-03-20
191TYPO

2nd para under The Unified …
“agens” s/b “agents”

2009-03-20
209TYPO

Macros first para end
Should be “features” i/o “features” ?

2009-03-20
209TYPO

Sorry - leave as “feature”. Or perhaps “a new feature” as opposed to “your own new feature”. I had no specific feature in mind when I read this sentence so it felt wrong.

2009-03-20
96TYPO

“Next, implement the a handler for the init method.”

Extraneous “the” here.

2009-03-20
70OK

I was reading through your book “Programming Clojure” the other day and on page 70 I found this code fragment

(defn index-filter [pred coll]
(when pred
(for [[idx elt] (indexed coll) :when (pred elt)] idx)))

Is the (when pred .. ) really required there? I am no expert on Clojure (hence I didn’t log a bug on the prag prg site) but it seems to me that the “when pred” is repeated twice in the function, once at the beginning of the function and once in the list comprehension. I tried a few simple examples at the repl and the variant witout the initial (when pred) seemed to work.

iow, this

(defn index-filter [pred coll]
(for [[idx elt] (indexed coll) :when (pred elt)] idx))

seemed to work for the simple examples I tried.

Of course I could be missing some deep Clojure magic (as I said, I am no expert and am extrapolating off my scheme and python knowledge to guess how the function works). In which case, I apologize for wasting your time, but maybe in the next version of the book you could add a sentence explaining why the (when pred) idea needs to be repeated twice in the code fragment.

Great book btw!
Cheers,

2009-03-20
95OK

In the :import section of the “ns” macro on this page you switch without explanation from the list-style imports on pages 64, 74, 92 to a using vectors. This has nothing to do with using “ns” vs. using “import” directly?

Perhaps the only real difference is that “import” is a function while “ns” is not. Therefore, the lists/vectors in “import” would typically be quoted to prevent evaluation. Unless you did something like this:
(import (list ’java.io ’File))

2009-03-20
69SUGGEST

… Apache Commons project, which is respected for quality….

I believe that you have no intent of sarcasm but the ‘respected for quality’ flies in the face of the example and made me wonder a bit. By dropping the ‘respected for quality’ adjective, the facts will stand by themselves and the chosen example is otherwise a fine item for improvement.

Thanks for the book.

2009-03-20
33TYPO

Excerpt:

Use dosync to add a visitor within a transaction:
(dosync (alter visitors conj “stu”))
#{“stu”}

“stu” here should be “Stu”. A few lines below visitors is derefed to show #{“Stu”}.

Cheers,
Sterling

2009-03-20
95ERROR

The example tasklist program included with the book code does not function as shown at the top of this page. In fact, there is nothing in the “classes” directory, so the bin/tasklist.sh script fails.

If the reader follows along with the text and creates his own reader.tasklist, then this runs fine. However, since Clojure uses a 1-pass compilation, the definition of the task-list function must be located before where it is referenced in -main. This is different from the order of presentation in the text.

2009-03-20
98TYPO

“You will need to have your classes on the classpath, plus clo-
jure.jar and clojure.contrib.jar.”

Should be clojure-contrib.jar

2009-03-20
111TYPO

“…implementation details of Lisp on original IBM 704 platform.”

Should be “the original”

2009-03-20
103TYPO

The latest version of Clojure in the book’s code bundle prints methods differently than shown in the book:
(#<public void … Mkdir.setDir(java.io.File)>)

is now:
(#<Method public void org.apache.tools.ant.taskdefs.Mkdir.setDir(java.io.File)>)

(Obviously disregarding the ellipsis.)

2009-03-20
35TYPO

The line “The book samples all unit tested” should be something like “The book samples are all unit tested”.

2009-03-20
55OK

It would be great if there were a small section about output , especially formatted output, in the book. Input/Output should ideall be dealt with early in the book.

2009-03-20
117OK

(apply str (interpose \\, [“apples” “bananas” “grapes”]))

Rather than a comma char, maybe the string “, ” to follow normal English convention following a comma with a single space.

2009-03-20
118SUGGEST

“For each collection type in Clojure, there is a function that converts a
list of arguments to that type:”

This is awkward to handle. Converting a list of arguments—the first thing that came to mind was something like this: (vector ’(1 2 3 4))

Your syntax diagrams make it clear, but maybe you should say something like “… a function that takes an arbitrary number of elements and creates a collection of the appropriate type containing those elements.”

2009-03-20
118TYPO

“vector also has a cousin, vec, that takes a single collection argument
instead of a varible argument list:”

“varible” -> “variable”

2009-03-20
119SUGGEST

It’s curious how whitespace is not needed to separate the tokens here: #{\\a\\e\\i\\o\\u}
This works in precious few cases: #{’a’b’c’d’e}
This by contrast is just one big keyword: #{:a:b:c:d:e}

Also, I’m nitpicking here, but f(x) doesn’t designate a function. It identifies the image of x under f, an element of the range of f:
“the function f(x) which tests to see if x is
vowel.”

2009-03-20
119SUGGEST

It seems a bit of a stretch to connect split-at to take-while and drop-while. There isn’t anything going on “while” some predicate is true. It simply splits the collection at a specified point. Maybe you should introduce it after split-with (which still should be tied to take/drop).

2009-03-20
120OK

" some returns the actual value of the first match instead of true."

I’m being picky here, but “some” returns the first non-false value returned by its predicate:
(some #(if (even? %) “Party on, Garth!” false) ’(1 3 5 4))

2009-03-20
121SUGGEST

You should probably point out that “map” can work with multiple sequences in parallel:
(map #(format “<%s>%s</%s>” %1 %2 %1) ‘(“h1” “h2” “h3” “h1”) ’(“the” “quick” “brown” “fox”))

2009-03-20
49ERROR

I suspect as a result of the change to lazyness in Clojure, the results of some example code is not as per the text any more.

user=> (interleave “Attack at midnight” “The purple elephant chortled”)
(\\A \\T \\t \\h \\t \\e \\a \\space \\c \\p \\k \\u \\space \\r \\a \\p \\t \\l \\space \\e \\m \\space \\i \\e \\d \\l \
\\e \\i \\p \\g \\h \\h \\a \\t \
)
user=> (str (interleave “Attack at midnight” “The purple elephant chortled”))
“clojure.core$concat_3174$cat3188$fn_3189@d4ea9f36”

2009-03-20
206TYPO

About half way down the page where it says “(line 3remembers”
should possibly be “(line 3) remembers”

2009-03-20
94TYPO

“For one-off tasks likes XML and thread callbacks…”

Should be “like” (singular).

2009-03-20
121TYPO

In the explanation of list comprehensions you mention “predicates on the elements” and then “an output function that produces output from the elements of the input lists that satisfy the predicate”.

In the second phrase, perhaps “the predicate” -> “these predicates”

2009-03-20
122SUGGEST

You don’t discuss the :while test in a list comprehension (anywhere in the book apparently):
(for [n (whole-numbers) :while (< n 50)] n)
You might want to emphasize that each collection can have its own :when/:while tests. A careful reader will pick this up from what you’ve written, but it might help to make it more explicit.

2009-03-20
263TYPO

The first two assertions in the :test metadata for index-of-any are identical:
(assert (nil? (index-of-any #{\\a} nil)))
(assert (nil? (index-of-any #{\\a} nil)))
(assert (nil? (index-of-any nil “foo”)))
(assert (nil? (index-of-any #{} “foo”)))
(assert (zero? (index-of-any #{\\z \\a} “zzabyycdxx”)))
(assert (= 3 (index-of-any #{\\b \\y} “zzabyycdxx”)))
(assert (nil? (index-of-any #{\\z} “aba”))))

2009-03-20
122OK

You might make a reference in the list comprehension discussion back to your function index-filter in section 2.6 to demonstrate the destructuring that ‘for’ can do.

2009-03-20
64TYPO

Top of the page the word namespace is hyphenated as names-
pace. Seems to me this is incorrect usage since the hyphenation doesn’t take place at a syllable boundary.

Cheers,

2009-03-31
140ERROR

(set-property! echo-task “message” “a new message!”) should be (set-property! echo-task :message “a new message”). We used “name” in property-descriptor in order to be able to use keyword property names

2009-03-20
121SUGGEST

You point out the connection between list comprehensions and set theory. It would be useful to give a concrete example of the different syntax. For example, your even natural numbers example:
(for [n (whole-numbers) :when (even? n)] n)
Would be:
{n|n < N, n is even}
(Where “<” should be the symbol for set membership, and “N” should be the symbol for the set of natural numbers)

2009-03-20
125SUGGEST

(cons 72 (.getBytes “ello”))

Would this be clearer?
(cons (int \\H) (.getBytes “ello”))

2009-03-20
127SUGGEST

It would be useful to discuss the ‘sort’ function a bit further here. For example, how do you sort in descending order, case-insensitive sort, etc…? You only have examples of ‘sort’ using the default comparator (ascending) in the book. The documentation at clojure.org is not helpful either.

2009-03-20
141OK

" After more than a decade dominated by object-oriented pro-
gramming, Clojure’s sequence library is the “Revenge of the Verbs.” "

To reinforce your earlier reference perhaps this should read: “… dominated by the nouns of object-oriented programming…”

2009-03-20
129SUGGEST

In the clojure-loc auxiliary functions ‘non-svn?’ and ‘clojure-source?’ wouldn’t it be preferable to use .getName as you did earlier rather than .toString? In principle this could generate a shorter string on which to search for the filename extension.

2009-03-20
129OK

I’m overdoing it here, but clojure-loc would be more accurate if it didn’t count comment lines. Also, placing the ‘non-svn?’ test first in the list comprehension should be more efficient since we’re testing the front of the string (maybe that depends on the relative number of “.svn” and “.clj” files…):
(defn comment-line? [line]
(if (re-find #“^\\s*;” line)
true
false))

(defn clojure-loc [base-file]
(reduce + (for [file (file-seq base-file)
:when (and (non-svn? file) (clojure-source? file))]
(with-open [rdr (reader file)]
(count (filter #(and (not (comment-line? %))
(non-blank? %)) (line-seq rdr)))) )))

2009-03-20
129ERROR

The function ‘non-svn?’ does not work as intended. First, by using .toString rather than .getName on File objects there will likely be path information in front of the “.svn” directory name, so trying to test with .startsWith will fail. Furthermore, even if .getName were used, the only File’s returned by file-seq that would match would be the “.svn” directories themselves. These directories would be weeded out, but their contents would not. It appears that all the SVN files at issue end in “.svn-work” or “.svn-base”. However, the “clojure-source?” function would eliminate these file anyway. Consequently, “non-svn?” appears to be irrelevant.

2009-03-20
130OK

It might be helpful to emphasize how xml-seq performs a depth-first traversal of the XML tree with literal content (strings) or empty elements as leaves.

2009-03-20
131SUGGEST

You don’t have a separate “Functions on Vectors” section, so it would be helpful to note here that ‘peek’ does not behave like ‘first’ for vectors. Rather, it behaves like ‘last’.

2009-03-20
131OK

The Clojure community has adopted a confusing convention of using the question mark as both a tag in predicate names (even?, zero?) and as a meta-character as in regular expressions (0 or 1 instances). Consequently, the following syntax diagram is difficult to read:
(get map key value-if-not-found?)
The clojure.org API page is more visually distinct:
(get map key)
(get map key not-found)

2009-03-20
135OK

Considering that strings are sequences in Clojure, is there a reason to do:
(select #(= 1 (.length %)) languages)
rather than:
(select #(= 1 (count %)) languages)

2009-03-20
134OK

The whole “Function on Sets” section is nicely done. You have 2 good examples (languages/beverages and composers/compositions) which are instructive and interesting. The section discussing relational algebra uses the concrete example particularly well to illustrate a potentially abstract topic.

2009-03-20
140SUGGEST

“Line 3 lets a property-descriptor…” Lets it do what? :)

Maybe clearer: “Line 3 creates a local variable pd in which to store a property-descriptor…”

2009-03-20
140SUGGEST

“Clojure functions that deal with side ef fects are often prefixed with do…”

Is it right to say this considering that all but dorun/doall are macros rather than functions?

2009-03-20
142OK

At the REPL you advocate restrictive ‘use’ forms to minimize namespace collisions:
(use ‘[clojure.contrib.except :only (throw-if)])
However, in the full code example, the ’ns’ macro doesn’t demonstrate this restraint:
(ns lancet.step-2-complete
(:use clojure.contrib.except)
(:import (java.beans Introspector)))

I believe this is the equivalent inside ‘ns’:
(:use (clojure.contrib [except :only (throw-if)]))

2009-03-20
141OK

You might mention when redefining instantiate-task that the newly-defined auxiliary functions must be placed before it in the source file (or ’declare’d). This is how you organize the source listing on page 142. But without you highlighting this issue it would be confusing for the user to encounter compiler errors if they simply appended set-property!, etc… at the bottom of their work from the previous chapter.

2009-03-20
147ERROR

" There is only one scope: the function’s argument list. "

Fortunately this is not completely true. Any nested function definition (let/letfn) can take advantage of variables in their enclosing scope. This is what makes closures interesting. I don’t think closures are inherently “impure”.

2009-03-20
219TYPO

“Begin the macro body with a syntax unquote (‘) to treat the entire thing as a template”

I think this paragraph should say “syntax quote”, rather than “syntax unquote”?

2009-03-20
151OK

You explain how stack-consuming-fibo is impractical due to its consumption of stack frames. You might also mention how inefficient it is due to redundant computation. For instance, computing F4 results in F1 being computed 3 times and F0 and F2 twice each.

2009-03-20
155TYPO

By working pairwise, it then calculates the Fibonaccis by carring along just
enough information (two values) to calculate the next value.

“carrying”

2009-03-20
63SUGGEST

I think a good and complete explanation of destructuring belongs in this book, rather than pointing to an online reference, for three reasons:

1. You can do a good job of explaining it.
2. It’s pervasive in clojure.
3. It makes the book more self contained.

2009-03-31
151SUGGEST

I recommend using the new ‘letfn’ for the local function in tail-fibo.

Also, “Structure and Interpretation of Computer Programs” (2e pg. 39) has a slightly more streamlined version of this Fibonacci implementation. In Clojure, with your naming conventions:
(defn tail-fibo [n]
(letfn [(fib [f-2 f-1 n]
(if (zero? n)
f-2
(fib f-1 (+ f-2 f-1) (dec n))))]
(fib 0 1 n)))

2009-03-20
153SUGGEST

“The only difference between tail-fibo and recur-fibo is on line 7, where
recur replaces the call to fib.”

Another minor difference is that the local function is actually anonymous here. Apparently ‘recur’ doesn’t need a name to know where to loop.

2009-03-20
153SUGGEST

Wow! It would really be helpful to include a note highlighting why this works:
([:a :b :c :d] 3)
Looking at the definition of fibo-series, unless you understand the above, this looks like a syntax error:
(series (- current 1))
Since Clojure is a Lisp-1 and the local variable ‘series’ is a sequence it doesn’t appear that you can use it as a function name…

You mention on pages 34 and 51 how sets and maps can be accessed as though they were functions by their elements/keys. I don’t recall you pointing out that vectors can be similarly indexed.

2009-03-20
183ERROR

The def of messages with the :validator…

(def messages (ref () :validator validate-message-list))

…gives me an error…

java.lang.IllegalArgumentException: Wrong number of args passed to: core$ref (NO_SOURCE_FILE:34)

In my REPL.

2009-03-31
186ERROR

Similar to page 183 a call to
(def counter (agent 0 :validator number?))

gives an error:

java.lang.IllegalArgumentException: Wrong number of args passed to: core$agent (NO_SOURCE_FILE:33)

I believe I’m using the latest version of clojure, but I’m not sure how to determine that.

2009-03-31
48SUGGEST

It would be best to avoid automatic hyphenation in literals, like “formfeed” (or is it “form-feed”?)

2009-03-31
50SUGGEST

Are the predicates also listed in a reference section of this book? If so, it would be good to reference that section here. Online documentation is nice, even essential these days, but books have advantages too, especially while you’re reading a hardcopy of said book away from a computer.

2009-03-31
158SUGGEST

“… if you accidentally hold the head (first item) of a sequence.”
This phrase isn’t an entirely accurate description of the issue. But Rich seems to like talking about “holding onto your head”. Strictly speaking, the head is the first item, as you state. However, the problem is holding onto a reference to the first cons cell whose car points to that item. It’s not the item itself but rather the sequence structure that is the problem. So “holding onto the head” of head-fibo means this: (def head (first head-fibo)). And, of course, there’s nothing wrong with that.

2009-03-31
158SUGGEST

“This is a very pretty definition,
in that it defines the recursion by mapping a sum over (each element of
the Fibonaccis) and (each element of the rest of the Fibonaccis.)”
It might be useful to illustrate this graphically:
-The first 2 elements of the sequence are 0 and 1.
-The 3rd and subsequent elements are computed as though these 2 infinite sequences were added:
(0 1 1 2 3 5 8 …) ;head-fibo
+(1 1 2 3 5 8 13 …) ;(rest head-fibo)
———————-
(1 2 3 5 8 13 21 …)

2009-03-31
159SUGGEST

You might remind the reader why in count-heads-pairs you are using the less perspicuous variable ‘cnt’ rather than ‘count’. You explain the issue of shadowing clearly on pg. 38. However, on pg. 153 you use ‘count’ as a local variable in fibo-series. Maybe that should be ‘cnt’ too. :)

2009-03-31
159SUGGEST

In count-heads-pairs, the following test:
(and (= :h (first coll)) (= :h (second coll)))
can be more succinctly written:
(= :h (first coll) (second coll))

2009-03-31
160SUGGEST

Is there some reason why by-pairs can’t be defined thus?
(defn by-pairs [coll] (map list coll (rest coll)))

2009-03-31
161SUGGEST

This may be too terse for illustrative purposes: :-)
(defn count-heads-pairs [coll]
(count (filter #(apply = :h %) (by-pairs coll))))

2009-03-31
163SUGGEST

For people new to functional programming it’s helpful to explain the origin of the term “curry”. If they don’t know who Haskell Curry was they get confused about spicy Indian food.

2009-03-31
15TYPO

“…how Clojure idioms differe from languages…”

2009-03-27
54TYPO

Figure 2.2 - the table of Reader Macros - is too wide for the page. The column entitled “Primary Coverage” extends out to the right past the margin and out of the page.

2009-04-29
83SUGGEST

The discussion of Clojure’s format is repeated on page 84. It probably only needs to be mentioned once and the section on Convenience Functions seems like a better place than here.

2009-03-31
166SUGGEST

“Clojure’s implementation uses a bitwise and to implement
odd? and even?”

The word ‘and’ should be displayed in a different font (as odd? and even?) are to indicate mention rather than use here.

2009-04-03
171TYPO

“I could not resist the temptation
to make the function more Clojurish in another ways as well:”

ways-> way

2009-03-31
168SUGGEST

After defining trampoline-fibo, it would be useful to explicitly highlight the correspondence between the shorthand notation:
#(fib f-1 f (inc current))
and its full anonymous version:
(fn [] (fib f-1 f (inc current)))
This illustrates how ‘trampoline’ expects a function of no arguments in order to continue the recursion.

You might also want to contrast this kind of closure (a function, ‘fib’, captured and called from outside of its defining scope) with the more common kind you discuss on pg. 58 (make-greeter creates a closure over a regular ‘data’ variable).

2009-03-31
168TYPO

" For self recursions like trampoline-fibo, trampo-
line of fers no advantage"

self-recursions

2009-03-31
169SUGGEST

" If not, you will be happy to have trampoline in your bag of tools. "

Don’t people usually carry tools in boxes and tricks in bags? :-)

2009-03-31
170SUGGEST

“… containing a single ’bottom element”
“…change ’bottom to ’deepest…”

Don’t really need to quote those symbols in the text (especially since you’ve used a different font). Looks kind of odd…

2009-03-31
171SUGGEST

When I first looked at Wallingford’s replace-symbol code I thought “This is overly complex. This function implements Common Lisp’s SUBST function. I’ve written it myself as an exercise using a single function. There’s no need for two.”

However, after reading Wallingford’s paper and seeing his rationale I realize that his version is actually quite elegant—following the BNF for the s-list datatype.

You’ve chosen a nice example here for which a lazy sequence provides a clear solution. The link to Wallingford’s paper is very useful too.

However, there are 3 issues:
1. Your idiomatic Clojure implementation is slick—no question about it—using multimethods. However, does that undermine the point he was trying to make about the code reflecting the type’s definition?
2. I’m not sure why you use when-let. The variable ‘s’ is not used anywhere. Wouldn’t it be more straightforward with just an ‘if’?
(if (seq coll)
(cons (replace-symbol (first coll) oldsym newsym)
(replace-symbol (rest coll) oldsym newsym))
‘())
3. The semantics of your version are not identical to the original. Both versions work the same if given legal data. However, yours will also handle a tree of numbers (1 (2 (3) 4) 5 1 ((3))). His will throw an exception if the ’scalar’ elements aren’t symbols.

2009-03-31
160SUGGEST

Why does take-pair bother supplying a name if (fn take-pair [c] … when take-pair does not call itself recursively?

2009-03-31
164TYPO

I don’t see why faux-curry is faux at all based on the wikipedia article on currying. Looking at that, it is not clear what the behavior of curry should be when applied to functions of arity less than 2. The way faux-curry behaves seems as reasonable a generalization as any, mathematically. If there is a reference on currying that can be footnoted here, that woud be nice.

2009-03-31
175TYPO

“Operations that are logically independent are easier to implement
if the platform can recognize and take advantage of their indepen-
dence.”

This is true, but it isn’t one of “several reasons that programs need to do more than one thing at a time”.

2009-04-03
173SUGGEST

In the section “Losing Your Head” you advise: " you should normally expose lazy sequences as a function that returns the sequence, not as a var that contains the sequence."

But here in the memoization section you state: “The final trick is to guarantee that the cache is built from the ground up by exposing sequences, instead of functions.”

Maybe a comment explaining that this does not contradict the earlier advice. Exposing the sequence is appropriate for the purpose here.

2009-04-03
177ERROR

“The ref wraps and protects access to its internal state. If you look at
the ref itself, you will not see its contents: ” ??

(def current-track (ref “Whatever Happened to Soy Bomb”))
current-track => #<Ref@fb494a: “Whatever Happened to Soy Bomb”>

(def pung (ref 9))
pung =>#<Ref@889cda: 9>

2009-04-03
178SUGGEST

Your description of the property of atomicity doesn’t address what seems to be its fundamental nature: all or none. A complex transaction can’t partly succeed. It completely succeeds or completely fails.

2009-04-03
179SUGGEST

“…four transactional properties are called the ACID”

Does ‘the’ belong there?

2009-04-03
179SUGGEST

“Users of the chat application want to see the most recent message first,
so a list is a good data structure.”

More precise?
“list” -> “stack”

2009-04-03
13TYPO

“Clojure is a power ful, general purpose programming language.”

general-purpose

2009-03-27
183SUGGEST

“Database transactions achieve consistency through various integrity
checks.”

Really nitpicking here, but should “achieve” be “maintain” instead? The point being that the system should never enter an inconsistent state.

2009-04-03
286SUGGEST

There is no entry for the ‘cond’ macro in the index. Also, there is an index entry “Functions, deeply nested”. This is a misleading reference to the function deeply-nested used on pg. 170.

2009-04-03
62TYPO

“It is also possible to simultanesouly - " should be ”simultaneously".

2009-04-03
189TYPO

In “Since only refs provide coordinate updates”, “coordinate” should be “coordinated”.

2009-04-03
284TYPO

(This is the Bibliography page.) The title of Hofstadter’s book should start with “Gödel” (with an o-umlaut), not “GÃ˝udel” (with an uppercase A-tilde and a lowercase u-Hungarian-umlaut which I couldn’t even copy properly into the browser). Also there is some junk after the bibliography list.

2009-04-29
183SUGGEST

The full syntax for ref (pg. 183), atom (pg. 184), and agent (pg. 186) should use ‘options*’ rather than ‘options?’. The notation conventions on pg. 16 state that the ? is associated with an optional arg rather than the zero or more possible with these functions (:validator, :meta).

2009-04-03
188SUGGEST

“You can check both the in memory messages…”

in-memory?

2009-04-03
188SUGGEST

“… backup only changed infor mation since the last backup.”

Maybe: only backup information that has changed since the last backup.

2009-04-03
189TYPO

There is a stray footnote number next to ‘send’ in Figure 6.1. This throws off the count so that the first actual footnote in chapter 6 on pg. 199 is numbered 2.

2009-05-27
189SUGGEST

“…from the peculiar semantics of each API:”

Maybe instead “the semantics peculiar to each API”

2009-04-03
191TYPO

“…stays in effect down any chain of calls that begin in the binding form…”

‘chain’ is singular => “any chain that begins” => “any chain of calls that begins”

2009-04-03
202SUGGEST

In the Snake GUI section multimethods are used before they have been introduced - there should be a reference to the multimethod chapter.

2009-04-03
140TYPO

[2nd prose paragraph, last part of last sentence] reads:

“so stuff in into a var named …”

thus, it should read:

“so stuff it into a var named …”

2009-04-03
191SUGGEST

A bit of a terminology conflict with the Lisp-specific concept of “special variables” here and the more general sense of the word ‘special’ as used on pg. 31 “Special Variables” e.g., *1, *2, *3. The index entry for “Special Variables” refers to the Lisp sense.

2009-04-29
47ERROR

The list of characters allowed for a symbol does not include <, >, or =; how is it then that >, >=, <, <=, =, and -> are valid symbols? (These name Clojure functions, at the least…)

2009-04-03
172TYPO

[2nd para., end of last sentence] reads:

“… make the function more Clojurish in another way*s* as well:”

I think you either meant this or that:
“… make the function more Clojurish in another way as well:”
“… make the function more Clojurish in other ways as well:”

2009-04-03
197TYPO

“Snakes are little bit more complicated”

“a little bit”

2009-04-03
198TYPO

“assoc retur n a new snake”

returns

2009-04-03
83TYPO

The result of to-array is an Object[], but the text shows a hybrid:
-> #<String[] [Ljava.lang.Object;1639f9e3> should be: -> #<Object[] [Ljava.lang.Object;1639f9e3>

2009-04-03
30TYPO

Missing space in “->#’user/hello”.

2009-04-03
16SUGGEST

The distinction in usage of `->’ and `|’ is not clear.
While `->’ seems to be used for values and exceptions, in contrast to console output, which is preceded by `|’, there are several examples that show otherwise:

page 61
(greet-author-1 {:last-name “Vinge” :first-name “Vernor”})
-> Hello, Vernor
(another one below)

page 91:
(wants-a-string “foo”)
-> foo
(wants-a-string 0)
-> 0

(several other examples may exist)

Although it is not clear to me, what is meant by `cannot easily be distinguished from code and results’, but similar snippets should be treated in the same way, e.g.

page 56
(date “Romeo” “Juliet” “Friar Lawrence” “Nurse”)
| Romeo and Juliet went out with 2 chaperones.

looks like any of the examples given above (ie. it is a function that prints to the console and does not have any meaningful return value).

2009-04-03
198SUGGEST

The section on the snake game discusses complex “bindings” in several spots. Back on page 63 you state: “The Snake game in Section 6.6, A Clojure Snake, on page 194 makes heavy use of destructuring.” Perhaps you should add a note in the snake section reminding the reader of the idea of destructuring, for example when discussing the ‘move’ function. As it is, the word “destructuring” doesn’t show up until the last page of this section.

2009-04-03
199SUGGEST

“It has none of the things that make code dif ficult to understand: global state, local variables, mutable state.”

The functional model of the snake game does make use of global state (free variables) in several places. However, since this state (height, width, win-length,…) is immutable there are no side effects to worry about. This reduces the complexity of understanding the non-local aspects of the code.

2009-04-03
202TYPO

“Swing calls the paintComponent (line 3) to draws the panel.”

draw

2009-04-03
202TYPO

“If they keyboard input was not an arrow key…”

they -> the

2009-04-03
202SUGGEST

“…the dirs function returns nil and update-direction does nothing.”

Strictly speaking, it’s the ‘turn’ function that does nothing. ‘update-direction’ calls ‘alter’ in any case.

2009-04-03
203SUGGEST

“please use your local operating-system fu”

Is that too slangy?

2009-04-03
206TYPO

“It is initially set to sentinel, which means that the target has not run yet”

Missing period at end of sentence.

2009-04-03
206TYPO

“If the result is sentinel, then this is the first caller, . Line 7 calls the function, and reset!s the result.”

Not clear whether this was meant to be 2 sentences.

2009-04-03
207SUGGEST

It would be helpful to demonstrate what the reader is supposed to do with the result returned by the ‘runonce’ function. This output must be captured for future use unlike defining a function via ‘defn’, for instance.

Also, there are several inconsistencies in the ‘runonce’ function. The ‘reset-fn’ and ‘has-run?’ functions are returned in the opposite order in which they are defined in the ‘let’ form. They also gratuitously use different syntax in their definitions—they are both anonymous functions without parameters. Why do they look different? Finally, the “main” function (the 3rd one) returned by ‘runonce’ isn’t even named at all as the other two are.

2009-04-03
205TYPO

Lancet is incorrectly capitalized twice on this page. It appears that every other instance of ‘lancet’ in the book is an appropriate case-sensitive occurrence.

2009-04-03
231SUGGEST

There are numerous references throughout the book to the term ‘DSL’, however, the word is only defined offhandedly at the end of chapter 3 on page 107. Even here, no connection is made between the initialism and its meaning. Section 7.5 should explicitly define what a DSL is.

2009-04-03
211TYPO

“Now you can clearly see that functions arguments are always evaluated…”

Either missing an apostrophe: “functions’ arguments” or maybe just “function arguments”

2009-04-03
211TYPO

Inconsistent hyphenation with “macro-expansion time”. Sometimes it appears as “macro expansion time”.

2009-04-03
34SUGGEST

Re: examples/introduction.clj…may I suggest a refactoring? Thusly (sorry in advance if this renders badly):

(def visitors (ref #{}))
(defn hello [username]
\t(if (@visitors username)
\t\t(str “Welcome back, ” username)
\t\t(do (dosync (alter visitors conj username))
\t\t\t(str “Hello, ” username))))

This:

- removes the unnecessary (for this example) setting of ‘past-visitor’, which is never used again, and removes the cognitive overhead of the reader having to figure out what the ‘let’ special form does for now

- shifts the critical section to be only the (alter…) (which has implications for the read above it, but the point is that’s the only part of the function that really requires thread safety; N.B.: why doesn’t (alter…) automatically execute inside a (dosync…)?)
- lets those of us who aren’t typing in every last snippet of code have all the context we need for the downloaded (or cut-n-pasted, in my case) “official” example.

Cheers,
David

2009-04-03
161SUGGEST

It’s worth mentioning that, even without the partition function, by-pairs could be defined much more simply as

(defn by-pairs [x] (map vector x (rest x)))

2009-04-03
214SUGGEST

“You do not want to quote expr and form, because they are macro
arguments. Clojure will substitute them without evaluation at macro
expansion time.”

This is a little confusing since the referent of the word they/them changes from one sentence to the next. In the first sentence, ‘they’ refers to the symbols ‘expr’ and ‘form’, which should be evaluated during macro expansion. In the second sentence, ‘them’ refers to the actual forms passed to the ‘unless’ macro. These are not evaluated during macro expansion.

2009-04-03
72TYPO

occcurence typo

2009-04-03
193TYPO

[5th prose paragraph, end of last sentence] reads:

“far from the point in you source where…”

but should read:

“far from the point in you*r* source where…”

2009-04-03
216SUGGEST

“Since when does not have to use its second argument as an else
clause, it is free to take a variable argument list and execute all
the arguments inside a do.”

This is kind of an unconvincing explanation. In any case, it’s really the 3rd arg (and subsequent args) that we’re discussing. The 2nd arg is the ‘then’ clause. It might be useful instead to remind the reader of the significance of evaluating multiple forms (implies side effects) as you did on page 67 discussing ‘do’.

2009-04-03
218TYPO

“Test that this new version of chain can correctly generate a single method call:”

“single-method”

2009-04-03
218SUGGEST

“(defmacro chain
([x form] `(. ~x ~form))
([x form & rest] `(chain (. ~x ~form) ~rest))) ”

For consistency with earlier versions of ‘chain’, the ‘rest’ parameter should be ‘more’. Same for top of next page.

2009-04-03
236SUGGEST

The definition of the deftarget macro can be made shorter and clearer if you first take care of the problem with the “def” special form: that giving something metadata requires use of a reader macro, which is problematic when defining a macro. So start by defining

(defmacro def-with-md [md sym & optional-init]
`(def (with-meta sym md)@optional-init))

which is a macro that could be useful in a lot of other macro-defining contexts. Then deftarget can be defined as

(defmacro deftarget [sym doc & forms]
`(let [[has-run-fn# reset-fn# once-fn#]
(runonce (fn [] ~@forms))]
(def-with-md {:has-run-fn has-run-fn#
:reset-fn reset-fn#
:doc ~doc} ~sym once-fn#)))

This avoids all the indirection with gensym.

2009-04-03
116SUGGEST

Use repeat instead replicate, because repeat now supports two arguments, and replicate is likely to be removed from the core.
See the thread at the google group (url: …browse_thread/thread/bb2bd6ad6986fedf/f0944ff8f9b7cf95)

2009-04-03
118TYPO

Missing space in “differently:set”.

2009-04-03
118SUGGEST

Place the hash-set example before introducing `set’ for a smoother flow.

2009-04-03
121TYPO

The color of the output of the second line in the second example on the page should be blue.

2009-05-27
121TYPO

[argument] -> element?

f is a function of two arguments. reduce applies f on the first two [arguments] in coll, then applies f to the result and the third [argument], and so on.

2009-04-03
125TYPO

“\\h” should be “\\H” in the fix for #38306

2009-04-03
220TYPO

“… it resolves the symbol to a fully qualified name.”

“fully-qualified”

2009-04-03
165ERROR

`curry’ should be `faux-curry’ here:
[…] adding an extra set of parentheses around the earlier expression:
(((curry true?) (= 1 1)))
=> true

2009-04-03
170TYPO

Missing space in `symbols.replace’.

2009-04-03
172SUGGEST

Use `when’ instead of `when-let’.
I don’t see the rationale of using when-let, since there is no reference to `s’. A simple `(when (seq coll) …)’ would suffice.

2009-04-03
191TYPO

Missing `|’ before console output.

(let [foo “let foo”] (print-foo))
10
(binding [foo “bound foo”] (print-foo))
bound foo

2009-04-03
211TYPO

Missing space between `if.unless’, also missing `|’ before console output in
(if (= 1 1) (println “yep, math still works today”))
yep, math still works today

2009-04-03
36TYPO

“Fully qualified names get old quickly” should be “Fully qualified names get odd quickly”

2009-04-29
155SUGGEST

Maybe it’s just me, but it took me a while to understand how using “lazy-seq to replace recursion with laziness” in lazy-seq-fibo worked. Maybe adding something like the following would help: The laziness turns the formerly recursive calls into a sequence of calls only as each element of the sequence is evaluated or realized.

2009-04-29
289SUGGEST

Index “letfn” as well as “let”.

2009-04-29
54TYPO

The primary coverage for the Anonymous function reader macro should probably be section 2.3 Anonymous functions, not 2.2 in figure 2.2.

2009-04-29
104TYPO

Were some of the line numbers on the ‘lancet/step_1_repl.clj’ code sample omitted on purpose?

2009-04-29
221SUGGEST

(let [a 1, b 2, bench-result (bench (+ a b))]
bench-result)

No real need to create bench-result variable here:
(let [a 1 b 2]
(bench (+ a b)))

Same with example below this one.

2009-04-29
221SUGGEST

Autogensyms now appear to have the string “auto” appended.
foo_1004 should be foo1004auto_

2009-05-27
218SUGGEST

In Figure 7.1 description of auto-gensym, should be “syntax-quoted section”?
Also, logically, should macroexpand-1 be listed before macroexpand? The description for macroexpand refers to macroexpand-1, which hasn’t been introduced in the figure yet (although the reader should be familiar with both from discussion on preceding pages).

2009-04-29
261TYPO

Text flows out in the bottom of the page.

2009-05-27
284TYPO

The ö in Gödel became `Ãu’.

2009-04-29
227ERROR

defenum/enum is not part of clojure-contrib right now.

2009-04-29
20SUGGEST

A few points about the Java/Clojure comparison, isBlank vs. blank?

1. The Java code treats null as blank. It doesn’t looks as if the Clojure code caters for this.

2. Treating a string of length zero as a special case is unnecessary in the Java code - going through the for loop zero times is fine.

3. In the Java code, use negation rather than comparison with false.

2009-04-29
205TYPO

Is it intentional that the args parameter(s) is never used after line 4 of the listing? Or should (function) on line 7 be (apply function args) ?

2009-05-27
65TYPO

(round 1.2) is shown as returning 2, but it actually returns 1.

2009-04-29
123TYPO

The REPL result of the rank and file list comprehension form isn’t marked as such. (It doesn’t have a leading “-> ” and isn’t blue.)

2009-04-29
247TYPO

“plain ’ol polymorphism” should be “plain ol’ polymorphism” since the apostrophe stands for the missing letter “d”.

2009-04-29
233TYPO

The example of calling (reset boo) shows a return value of nil. The reset-fn of runonce actually returns the value of the call (reset! result sentinel) which in this case is the vale of sentinel. On my system that shows up as something like:

user> (reset boo)
#<Object java.lang.Object@4ce6bd1a>

2009-04-29
132TYPO

([:a :b :c] 1)
-> :a
should be
-> :b

2009-04-29
278ERROR

At the end of the Posts and Redirect section I couldn’t “create a few snippets” because the code emitted by (new-snippet) wasn’t recognized as HTML, at least by Safari 4 beta. I change the (html) call to emit

and that worked. Adding the layout code in the subsequent section also worked.

2009-05-27
222SUGGEST

“All flow control macros must eventually call a spe-
cial form.”

eventually -> ultimately ?

2009-04-29
110TYPO

Shouldn’t “(rest aseq)” be “next aseq”?

2009-04-29
50TYPO

The regular expression given to find-doc, to find all predicates, seems to be too inclusive. It appears
to have a space after the backslash, so that it does not
escape the question mark (which I think is what is intended).

2009-04-29
57TYPO

I don’t believe that the vector form of the ‘use’ function, used near the top of the page, has been seen before and it may not be obvious what it is doing. A 1/2 sentence of explanation might be useful here.

2009-04-29
57TYPO

Shouldn’t the syntax of anonymous functions be described as
#(body), rather than #body? It looks like anonymous functions are read in by a reader macro, which I suspect is triggered by the ‘#(’ character sequence. Can any other characters follow the sharp sign and still create an anonymous function? If not, then I think the parentheses should be part of the syntax specification.

2009-04-29
51TYPO

-> “I dunno!
should read
-> ”I dunno!"

It is missing a quote at the end

2009-04-29
57TYPO

Not a huge omission, but the sample text to each of the filter examples is “A fine day it is” except the one right after the (fn [params*] body) which is just “A fine day”. It doesn’t change the output or anything but looks strange given the surrounding examples.

2009-04-29
135TYPO

The font for the examples of ‘assoc’, ‘dissoc’ and ‘merge’ change from the blue repl output to red repl input in the middle of repl output.

2009-05-27
56SUGGEST

“Writing method implementations that differ by arity is useful. But if you come from an object-oriented background, you will want polymorphism, […]” I think “Writing functions that differ by arity …” would be better, since you are discussing functions here, not methods. The following sentence, which describes the object-oriented view, would still make sense.

2009-08-16
34SUGGEST

The comment in line 2 of examples/introduction.clj states
"Writes hello message to out
Actually, it returns a String. No printing to standard out is done (when used outside the REPL).

2009-08-15
36TYPO

first line on page says
“ples.introduction.fibs.”
That should be
“ples.introduction/fibs.”

2009-08-15
55TYPO

mid page:
“If you want to make greeting issue a generic greeting when the caller omits name,…”
should be
“If you want to make greeting issue a generic greeting when the caller omits username,…”

2009-08-16
67TYPO

top of page

“| Saw a big number 200”

should be (remove a blank)

“| Saw a big number 200”

2009-08-16
74TYPO

Near mid-page:

To add your own key/value pairs to a var, use the metadata
reader macro:

metadata should be in italic

2009-08-16
77TYPO

gen-and-save-class doesn’t seem to exist

2009-08-16
256TYPO

collection-tag definition is out of dat

2009-08-16
152ERROR

The analysis of stack-consuming-fibo is a bit misleading. It generates a number of stack frames exponential in n. (This is also proportional to n, but that is a much weaker statement). There is also a small grammar error in that sentence.

2009-08-16
116TYPO

Near top:
“The repeat function repeats an element x n times:”

repeat should be in italics

2009-08-16
94SUGGEST

The example code scripts in bin only work under bash. Under csh or tcsh, they immediately error out. One easy way to make the scripts more compatible would be to insert the line:
#!/bin/sh
as the 1st line of every script.

2009-08-16
24SUGGEST

The common lisp code goes across a page break and is thus hard to read. I suggest reformatting it so it all comes on one page.

2009-08-16
29TYPO

Quoting a text placed at the end of the page:

“[…] You do not have to worry about any of this, because the sample code includes all these files and the REPL launch scripts place them on the classpath.”

2009-08-16
33SUGGEST

if is rendered as a keyword in 3rd line of examples/introduction.clj

2009-08-16
36TYPO

Referencing the namespaced version of fibs, the text has “examples.introduction.fibs” whereas it should be “examples.introduction/fibs”

2009-08-15
1ERROR

In the repl.bat file in the code download each REM command is lacking a space after it so you get errors running the file.

2009-08-15
39TYPO

wrt to “Anonymous Functions”, the book states “The syntax looks like this: (#body)”, should be #(body).
p39, line 8.

2009-08-16
188ERROR

the example create-runonce defines the (fn [& args]) but doesn’t actually apply the args to the function. this makes the example in the book do nothing but print a newline.

the (reset! result (function))
should be
(reset! result (apply function args))

this will make it work

2009-08-16
28ERROR

It is mentioned twice that “Clojure relies on Java’s BigDecimal class for arbitrary-precision decimal numbers.”

The first time with a reference to the bottom of the page and the second time with the URL directly.

2009-08-16
76ERROR

- You can also pass a nonstring to shout and see that

- Clojure enforces the :tag by attempting to cast the
- argument to a string"

Nope, otherwise this would fail too:

user> (defn #{:tag String} shout [#{:tag String} s] s)
user> (shout 10)
10

The :tag definition allows Clojure to select .toUpperCase from String class in compile time and when shout is invoked ClassCastException is thrown because it is invoked on instance of wrong type.

Without :tag definition .toUpperCase must be resolved during shout invocation which fails if not defined for given argument class.

2009-08-16
46TYPO

On the bottom third of the page, after “Now you can call round without having to qualify its name:”, the code example is,

(round 1.2)
=> 2

The result is actually 1, not 2.

2009-08-16
104124ERROR

(for [n (whole-numbers) :while (even? n)] n) yields an empty list and not (0) as the seq from (whole-numbers) starts from 1. (sugg: replace even? by odd? to get (1) as an example)

2009-08-16
223ERROR

In the following text,

“. . . until the unlucky day that you picked a local name like start, which
collided with a name inside bench:
(let [start 1 end 2]
(bench (+ start end)))
) {:result 3, :elapsed 1228277342451783002}}
bench captures the symbol start and binds it to (System/nanoTime). All of
a sudden, one plus two seems to equal 1228277342451783002.”

Shoudn’t the result be along the lines of:
{:result 1228277342451783002, :elapsed 39000}} ?

2009-08-16
33TYPO

Just a silly typo… The first call to “(hello ”Stu“)” doesn’t show the correct output from the REPL. That is, it doesn’t have the " marks. The second call does.

2009-08-16
66ERROR

incorrect result of (round 1.2) shown. It shows 2 when it’s 1.

2009-08-16
72ERROR

(index-filter #{\\a \\b} “xyz”) returned () instead of nil

2009-08-16
81ERROR

user=> (import ‘(java.util Random Locale) ’(java.text MessageFormat))
java.text.MessageFormat

Hence, the last import was returned instead of nil…

2009-08-16
192ERROR

There’s a bug in Clojure 1.1.0-alpha-SNAPSHOT that breaks binding. So with the code download for the book the examples don’t work as they should. e.g. I get the following:

user=> (def foo 10)
#’user/foo
user=> foo
10
user=> (.start (Thread. (fn [] (println foo))))
10
nil
user=> (binding [foo 42] foo)
10
user=> (defn print-foo [] (println foo))
#’user/print-foo
user=> (print-foo)
10
nil

Note that the binding didn’t update the value of foo to 42 and returned the root binding 10 instead…

2009-08-16
153TYPO

I still get StackOverflowExceptions for n = 10000, although the presented solution is supposed to fix it. But the cuplrit is actually not “replace-symbol”, but “deeply-nested”.

2009-08-16
208ERROR

as written create-runonce can’t be used to create and run println-once the way it is shown. create-runonce returns a function that when executed with parameters doesn’t pass the parameters to the wrapped function. So when creating println-once and calling it with (println-once “there can be only one!”) will yeild a blank line and nil. The blank line is calling println without args (i.e. (reset! sentinel (function)) in the code) and nil is the result of the function (println returns nil)

create-runonce needs to be modified to pass the arguments through the anonymous function to the wrappered function (function).

Try the following instead:
(defn create-runonce [function]
(let [sentinel (Object.) result (atom sentinel)]
(fn [& args]
(locking sentinel
(if (= result sentinel) (reset! result (apply function args)) result)))))

user=> (defn create-runonce [function]
(let [sentinel (Object.) result (atom sentinel)]
(fn [& args]
(locking sentinel
(if (= result sentinel) (reset! result (apply function args)) result)))))
#’user/create-runonce
user=> (def println-runonce (create-runonce println))
#’user/println-runonce
user=> (println-runonce “there can be only one!”)
there can be only one!
nil
user=> (println-runonce “again?”)
nil
user=> (println “testing”)
testing
nil
user=>

2009-08-16
72SUGGEST

The code given for index-filter contains a redundant use of ‘when’.

The code is given as:
(defn index-filter [pred coll]
(when pred
(for [[idx elt] (indexed coll) :when (pred elt)] idx)))

The ‘when’ is not needed. The following suffices:
(defn index-filter [pred coll]
(for [[idx elt] (indexed coll) :when (pred elt)] idx))

The sequence comprehension already checks that the predicate holds.

2009-08-16
223TYPO

sample doesn’t match text:
——-
(let [start 1 end 2]
(bench (+ start end)))
{:result 3, :elapsed 1228277342451783002}}

bench captures the symbol start and binds it to (System/nanoTime). All of
a sudden, one plus two seems to equal 1228277342451783002.
——-

In the text it says the result is 1228277… but in the code sample the elapsed is 128277…..

2009-08-16
233TYPO

Not sure if this is a typo or something I don’t understand (as it works fine). The bench-fn example has a superfluous “>” symol in it. Here’s the example from the book:
(bench-fn (fn []> (+ 1 2)))

The > symbol is after the parameter list for the anonymous function. It seems to work the same with or without the > so I’m not certain it’s wrong…

2009-08-16
259SUGGEST

It is non-obvious how to run the (.setDir mkdir-task “sample dir”) from a new REPL (or fairly clean one). For example, one would think the following would work:

user=> (use ’lancet.step-4-complete)
nil
user=> (.setDir mkdir-task “sample-dir”)
java.lang.Exception: Unable to resolve symbol: mkdir-task in this context (NO_SOURCE_FILE:0)

However, mkdir-task is undefined… The answer lies all the way back in section 3.5 with the following def:

user=> (def mkdir-task (org.apache.tools.ant.taskdefs.Mkdir.))
#’user/mkdir-task
user=> (.setDir mkdir-task “sample-dir”)
java.lang.ClassCastException (NO_SOURCE_FILE:0)

It would be nice to have the code repeated here for easily following along at the REPL or a pointer back to that section…

2009-08-16
273ERROR

The following call for the modified select-snippets doesn’t need the with-connection:
(with-connection db (select-snippets))

with-connection was just added inside select-snippets itself, so wrapping it in a with-connection is redundant.

2009-08-16
124ERROR

The evaluation of (for [n (whole-numbers) :while (even? n)] n) is listed as (0). I believe it should be () as 0 is not included in the whole-numbers list.

2009-08-16
40ERROR

The function clojure.contrib.repl-utils/source does not work with the version of clojure.jar included with the book source code download (which appears to be an alpha build of clojure 1.1.0). It does work with clojure-1.0.0.jar.

2009-08-16
203TYPO

The second code snippet still has result 3 not 1228277342451783002 as stated in the next line.

2009-08-16
154ERROR

At the bottom of the page:

= Line 2 introduces the letfn macro: (letfn fnspecs & body) ; fnspecs > (fname [params*] exprs)+
===

The last “fnspecs” is incorrect. The online documentation calls this “fnspec” and says that fnspecs is a vector of fnspec.

Suggested change:

= Line 2 introduces the letfn macro: (letfn fnspecs & body) ; fnspecs > [(fname [params*] exprs)+]
===

2009-08-16
32ERROR

Clojure source should be obtained via:
$ git clone git://github.com/richhickey/clojure.git

2009-08-21
118SUGGEST

When the “repeat” function is first introduced on the 3rd line (The repeat function repeats an…), its name is written with the “normal text” font, instead of the monospaced one normally used for function names (as it’s done two lines later in “Try to repeat some items…”).

2009-10-20
13TYPO

The output of the first (hello “Stu”) is missing the double quotes, it should be:
“Hello, Stu”

The output of the second (hello “Stu”) on that page is correct.

2010-06-16
18TYPO

The first paragraph mentions examples.introduction.fibs and it should probably be examples.introduction/fibs.

2010-06-16
93OK

The following statement does not seem to be true :

(next aseq) is equivalent to (seq (rest aseq))

2009-10-20
93OK

Sorry, my bad…. just double-checked again, the statement in the book is correct. Sorry.

2009-10-20
36DEFER

In the example of redefining the hello function to be hello-with-memory, the text says you can verify the new functionality by typing (hello “Rich”) and then typing (hello “Rich”) again. If you use (load-file “examples/introduction.clj”) to automatically enter the example code instead of typing it in by hand, however, the example fails, because the example file defines everything in the “examples.introduction” namespace. When you type just (hello “Name”), you’re really getting (user/hello “Name”), which is the simpler function you typed in before. The new function lives in “examples.introduction/hello”.

Typing (examples.introduction/hello “Rich”) twice in a row does display the new functionality.

This is a good point for the next edition.
134ERROR

“The stack-consuming-fibo creates a number of stack frames proportional to n”

n should be (2 to the n) because each call generates two calls.

2010-06-16Rephrased in second printing.
23DEFER

In
(use ’lancet)
where is lancet? The code does not load as is.
Also ’lancet.ant.

This example code showing lancet usage is not intended to be run at this point in the book. To see where lancet has gone since the book has been published, see http://github.com/technomancy/leiningen
172ERROR

Using the “binding” form does not work properly on Mac OS Snow Leopard using the code downloaded from the Programming Clojure website.

(def foo 10)
=> #’user/form

(binding [foo 42] foo)
=> 10 (should be 42)

The Clojure version reported by the REPL is 1.1.0-alpha-SNAPSHOT.

2010-06-16This was a one-off issue with a few builds of Clojure, now fixed. Please get the source code from http://github.com/stuarthalloway/programming-clojure if possible.
223TYPO

When talking about auto-gensym, the “try it” example is `foo#. The font in the PDF makes this appear like ’foo#. Copying gives the correct backtick, however.

292TYPO

under lemma Refs, the second line reads “concurrrency model and”, which I guess should be “concurrency model and”.

2010-06-16
TitleTYPO

The “Version of Book With Error” combo box on the “Add Erratum for Programming Clojure” webpage (i.e. the page I am filling this out on) states that the publication version P2.0 date is Oct 20, 2009.

My copy of the book says:
P2.0 printing, September 2009
Version: 2009-9-16

There is no Sept 16, 2009: P2.0 value in the combo box.

189OK

Line 8 of the runonce code has a nil that looks like it can be omitted.

2010-07-19Returning nil prevents reset-fn from exposing implementation details.
209OK

line 8 of the runonce code has a nil that could be omitted.

2010-07-19Returning nil prevents reset-fn from exposing implementation details.
208OK

(function) should be (apply function args)
This was reported for v. 1.0 but not yet corrected.

2010-07-19This is not a problem once #41579 is fixed. Thanks!
223203OK

The example shows what would happen if Clojure allowed unqualified symbols “start” and “result” in a macro. The example continues with “start” being bound to the user’s value, 1. This gives an erroneous result on the time it takes to add the numbers, NOT the result of adding the numbers.

So, in the middle of the page, “All of
a sudden, one plus two seems to equal 1228277342451783002.” should be “All of
a sudden, adding one plus two seems to take 1228277342451783002 nanoseconds.”

2010-07-19The macro trumps the let binding, not the other way around. Try it and see.
208OK

The definition of println-once should be
(def println-once (create-runonce println))
as in was in the first printing assuming the code for runonce is changed to (apply function args).

2010-07-19This is not a problem once #41579 is fixed. Thanks!
208TYPO

I see what you are doing now. But if you leave line 7 of the code with (function) and include the argument to println in the def of println-once then you should omit the argument in the two calls to println-once at the botgtom of the page

2010-06-16
52OK

This may be a misunderstanding on my part, but for the code example:
[code]
(defn index-filter [pred coll]
(when pred
(for [[idx elt] (indexed coll) :when (pred elt)] idx)))
[/code]

the book states “The index/element pairs of (indexed coll) are bound to the names idx and elt but only when (pred elt) is true.”

It seems that elt, at the very least, must get bound regardless in order to evaluate whether (pred elt) is true; it does not make sense to me that it could base its decision to bind or not based on whether an as-yet-unbound name satisfies a predicate.

Alternately, if it is clever enough to somehow recognize “I only want to bind values to [idx elt] if the value that would be bound to elt satisfies pred, so I will test pred with that value.” then that should probably be explicitly stated, though I am not sure that that is actually different than binding.

2010-07-19I believe the existing text is simple and accurate.
52OK

In the definition of index-filter, what is the purpose of the outer when?

2010-07-19Prevent NullPointerExceptions when passed nil.
31DEFER

The remark about “following the *nix instructions” for Cygwin is not accurate. From a Cygwin shell, the repl.sh script does not work. You must change the -cp argument (quote it and change colons to semicolons). The reason is that java for Windows uses a different path separator.

145SUGGEST

The instantiate-task function does not need to add task at the end, since (doto task exp1 exp2 ..) returns task implicitly

2010-06-16This was fixed prior to the first printing.
223TYPO

While describing about symbol capture using the bench macro, the explanation seems correct but the code has a typo.

(let [start 1 end 2]
(bench (+ start end)))

{:result 3, :elapsed 1228277342451783002}}

Here result should be 1228277342451783002 and :elapsed time should be different. The text following this gives the correct description though.

2010-06-16Fixed in second printing.
84TYPO

In there paragraph right after the (make-array String 5) example, you state that “[Ljava.lang.String” is the JVM spec’s encoding for an array of String objects.

The semicolon is missing from that. The encoding should be “[Ljava.lang.String;” as the semicolon matches the starting “L”.

2010-06-16
74OK

The phrase, metadata is “data about data”, is technically incorrect. Although it is more common in English to do so, the preferred is still to treat data as plural.

Thus it should read metadata are “data about data”.

The wikipedia says “the word metadata is” which is singular because it is referring to “the word” and not “metadata”.

2010-07-19Author's choice.
52ERROR

(index-filter #{\\a \\b} “xyz”)

returns (), not nil.

2010-06-16This was fixed in the second printing.
73TYPO

At the footnote at the bottom of the page, you say you could write an “functional indexForAny” in java, but it should be “indexOfAny” to agree with the rest of the text.

2010-06-16
39TYPO

(#body) instead of #(body)

2010-06-16
27SUGGEST

This is about “cond”. I got the impression that Clojure supports the Common Lisp cond form (with parentheses). But when I tried typing the example code, I got an error:

(cond
((< x 10) “x is less than 10”)
((> x 10) “x is greater than 10”)
(:else “x IS 10”))
=> cond requires an even number of forms
[Thrown class java.lang.IllegalArgumentException]

After reading the paragraphs on cond, I thought Clojure supported both the Common Lisp form that had parentheses and the form that didn’t have parentheses. Maybe the paragraph can have a note about that? I wouldn’t have understood this without typing in the example.

36TYPO

The if in a comment block is colored.

236TYPO

A normal quote is used in the first listing of deftarget rather than a back-quote.

75SUGGEST

The metadata reader macros described have been deprecated with the release of Clojure 1.2 — ‘’ is now used to attach metadata to objects (with a shortcut for type hinting), and ’#’ is no longer supported.

39SUGGEST

In the first anonymous fn example, “A fine day” string is used.
Instead, to be consistent with other examples nearby, “A fine day it is” ought to be used.

97TYPO

The text states, “The interesting part is the :gen-classform….”

I believe it should read “:gen-class form”.

52ERROR

In the second example of the use of index-filter:

(index-filter #{\\a \\b} “xyz”)

should the return value be “()” (an empty list) instead of “nil”?

That’s what I get, and what I’d expect.

171151TYPO

Under subheading, “Replacing Recursion with Laziness,”

(replace ’((a b) (((b g r) (f r)) c (d e)) b) ’b ’a)

is shown to be returning:

((a a) (((a g r) (f r)) c (d e)) a)

but this throws an exception:

“Wrong number of args (3) passed to: core$replace
[Thrown class java.lang.IllegalArgumentException]”

39TYPO

For for anonymous function using implicit parameter names is “(#body)”. Should be “#(body)”.

122ERROR

It says: “To see a non-true match, try using some with identity to find the first non-nil value in a sequence:
(some identity [nil false 1 nil 2])
⇒1”

Shouldn’t that be “the first value in a sequence that evaluates to true”? Because otherwise it would return false wouldn’t it?

153TYPO

Unsure if my previous attempt to submit this worked as it complained about use of a hyperlink. There’s a problem with the typesetting of the kindle version which is not a problem in the PDF. The subscripts don’t work properly - the subscript text doesn’t appear and the text which follows is in a smaller font. there’s an image at dl.dropbox.com/u/6538629/Subscripts.JPG

41ERROR

(show java.util.HashMap) did not work

38ERROR

The example near the top of the page is (take 10 examples.introduction/fibs). The actual expression should be (take 10 (examples.introduction/fibs)).

38ERROR

Sorry.

The other examples using fibs on this page are also incorrect. The text on the pages is (take 10 fibs). The example should be (take 10 (fibs)).

39ERROR

The example near the bottom of the page defines the function “hello” in the namespace examples.introduction. This function was also defined on page 36 with a different implementation. If you are trying to re-create the sample code instead of simply reading it, you must overwrite the previous version. I do not believe this consequence was intended.

40SUGGEST

I believe the “source” function is now defined is clojure.repl.

57SUGGEST

On page 56, we define the function “greeting” in the file exploring.clj. On page 57, we redefine the function “greeting” to accept multiple arguments.

It might be clearer to name these two functions differently.

59SUGGEST

On page 58, we define “indexable-words?” as a top-level function. On page 59, we reuse that name but bind it to a locally defined function.

A footnote might be nice indicating that the second usage shadows the first.

73ERROR

On page 73, we define a function named describe-class. This function clashes with the version defined on the previous page. If we want to maintain both, I believe we need two different names.

78SUGGEST

On page 77, we provide sample code for reader/tasklist.clj. On page 78, we then shift to example/tasklist.clj. For several minutes, I thought these we two different files needed to compile a Clojure class. Eventually, I read paragraph 4 on page 78 and discovered that files in example illustrate the provided sample code but files in reader/tasklist.clj are code that I might enter. I not mentioning the difference between code in reader and example until almost a page after introducing reader confusing.

84ERROR

We define class-available? near the middle of the page and then we redefine it a paragraph later.

106SUGGEST

On this page, we define the symbol x twice. When I was first entering this data myself, I did not (since I’d just defined x). When I entered the function (dorun x), I DID NOT see the same result as displayed in the book. Although I eventually determined the reason for this difference, I think it would be worthwhile to emphasize the need to define this symbol twice.

203ERROR

When I tried the examples on this page, the Clojure repl reported an error:

user=> (let [a 1 b 2] (broken-bench (+ a b)))
java.lang.Exception: Can’t let qualified name: examples.macros.bench-1/start (NO
_SOURCE_FILE:13)user=>

user=>

I am using Clojure 1.2.

92TYPO

Code example at bottom of page reads:

(parse (java.io.File. “examples/sequences/compositions.xml”))

However, the proper path to compositions.xml is “data/sequences/compositions.xml”

Categories: