By Developers, For Developers
| PDF Pg | Paper Pg | Type | Description | Fixed on | Comments |
|---|---|---|---|---|---|
| v | OK | “In 1989 I started …” A nod to Grace Hopper might not be out of place; she was pushing concurrency way before 1989. I’d also like to see a table that shows which concurrency and parallelism So, actors, agents, queues, reducers (etc) would be on one axis; | 2014-04-21 | ||
| 79 | ERROR | The definition of the translate function is missing a closing parenthesis (has three, should have four). | 2013-11-07 | ||
| 80 | ERROR | The definition of the translate function is missing a closing parenthesis (has three, should have four). | 2013-11-07 | ||
| 93 | TYPO | I’m reading a PDF. The page number printed on the page is 93, but it is page 102 of the file. The error occurs in the first sentence. “A validator is a function that is called whenever there’s an attempt to change the value the atom.” should be, “A validator is a function that is called whenever there’s an attempt to change the value of the atom.” | 2013-11-07 | ||
| 53 | TYPO | public int sum(int[] numbers) { | 2013-11-26 | ||
| 85 | TYPO | “Clojure provides a similar hybrid of functional programming with and mutable state” Probably only need one of “with and”. | 2013-12-01 | ||
| 59 | ERROR | “Here’s count-words. Given a sequence of pages, it returns a map of the frequencies of the words in those pages: […] It starts by converting the sequence of pages into a sequence of words with (mapcat get-words pages).” This example does not use `mapcat`. It also introduces pmap, which seems odd considering that pmap is parallel and this example should be sequential; pmap is also talked about in the self-study. On page 63, this code is referred to again and is explicitly said to be sequential | 2013-12-01 | Thanks Jake, \n \nAs you'll see in the next beta update, this chapter has been significantly rewritten. Some of those changes leaked into the last update, for which apologies. Hopefully it should now make sense again. \n \nThanks! | |
| 85 | SUGGEST | May want to add references to core.async[1] and pulsar[2]. The former is “A Clojure library designed to provide facilities for async programming and communication.”, the latter is “Pulsar is a Clojure library that provides high-performance lightweight threads and Erlang-like actors”. [1]: github.com/clojure/core.async | 2013-12-01 | As it happens, we've recently decided to add a chapter that covers core.async (in place of the previously planned Tuple Spaces chapter). Look for it in a beta update before the end of the year. | |
| 108 | TYPO | The body text references on the page refer to function name with trailing exclamation marks, !, but the code examples to not have them. | 2013-12-01 | Thanks Bo! | |
| 105 | TYPO | Before: Checking = 10000 Savings = 20000 The “Transfers” count should be 200. | 2013-12-14 | Thanks Daniel, \n \nThe error's actually in the description - it says that one thread transfers $100 100 times, and the other $200 100 times. In fact the second thread actually transfers $100 200 times. \n \nThanks for pointing it out - I'll fix the description in the next beta. | |
| 108 | TYPO | #53263 not fixed in B4.0. Still exclamation mark where they don’t belong. | 2013-12-23 | Thanks Bo. I fixed a couple of these, but also missed a couple. Sorry about that (and thanks for pointing it out again). | |
| 54 | ERROR | Where Does Reduce Get Its Initial Value? From the Clojure docs[1]: “If val is not supplied, returns the result of applying f to the first 2 items in coll” The identity usefulness comes into play with the reducers library, however. [1]: clojuredocs.org/clojure_core/clojure.core/reduce | 2014-01-18 | Good point - I'll move this discussion to the section on reducers rather than reduce. Thanks. \n \nUpdate: I ended up changing the description to "What If We Pass An Empty Vector To reduce?" for which the identity value is used, even in vanilla reduce. Thanks again Jake. | |
| 56 | SUGGEST | “We start by creating an array” Clojure tends to refer to [] as a “vector”. “array” might be confusing, as Clojure tends to reserve that term for Java primitive arrays. However, Java also has “Vector”, so both are potentially confusing. :-) I’d stick with the Clojure terms to help people when searching later. | 2014-01-18 | Good point. Will do. Thanks. | |
| 56 | OK | “As is often the case with code running on the JVM, we have to run more than once” You may want to use or mention the generally-approved Clojure benchmarking library `criterium`: github.com/hugoduncan/criterium | 2014-04-21 | Thanks. I'm a big fan of Criterium, but wanted to keep things simple for the reader here. I'll discuss this with Jackie (my editor) and see what she thinks. Thanks again. | |
| 61 | ERROR | “Arguments are specified with %1, 2, and so on, which can be further shortened to a single if each argument is used exactly once and in order.” `%` is an alias for `%1`, but should only be used when there is exactly one argument: (map #(+ %1 %2) [1 2] [3 4]) (map #(+ % %) [1 2] [3 4]) | 2014-01-18 | Ah! Thank you, I had assumed that % in Clojure worked like _ in Scala. Clearly not. Thanks again. | |
| 174 | TYPO | First line read: | 2014-03-07 | ||
| 23 | SUGGEST | Shouldn’t the l2 unlock be in the given finally block and the l1 unlock be in a finally block of the outside try block? | 2014-03-22 | Sigh. Yeah, strictly it should be - this was a misguided attempt on my part to do something to make the code more comprehensible in the face of Java's ludicrous checked exceptions. \n \nThe problem is that I'm trying to get the code to exit cleanly, and if I write the strictly correct code it doesn't (it throws an IllegalMonitorStateException before it gets a chance to process the InterruptedException, because the unlock() fails.). The "correct" code ends up being a maze of twisty try/catches which obscures the basic point. \n \nI suspect that the right solution is to ditch the unlock calls entirely - otherwise I'm confusing matters more than I'm helping. \n \nThanks! | |
| 25 | SUGGEST | Could you put lock numbers on the hand-over-hand diagram? This might make the concept of lock over the next one more clear. Also, is there a reason why you didn’t start the diagram by locking the head node? | 2014-04-13 | Hmm - won't the lock numbers just be the index of the node in the list? Am I missing something? \n \nAs for why didn't I start at the head node, I guess I didn't really think about it particularly carefully - I just started a couple of steps before the point where I was going to end up inserting the new node :-) How confusing did your reading group find this? | |
| 29 | OK | Should the “stop eating” code be removed from the think() method and put at the end of the eat() method? (Or be moved to its own method?). Currently think() violates the SRP. | 2014-04-21 | Whether it violates the SRP depends on how you think of the "stop eating" code. Each philosopher is a very simple state machine with two states. If you think of the code that you describe as "stop eating" as "start thinking", then the SRP holds :-) \n \nI'm slightly wary of moving this code into eat(), as that method then gets rather long. And I'm slightly wary of creating another method as it's already (thanks to Java's crazily verbose syntax) rather longer than I'd prefer. And f I move "stop eating" into its own method, I probably also need to move "start eating" into its own method, making things even more verbose... | |
| 32 | SUGGEST | Suggestion to add to the self-study: what would happen if there are multiple conditions in your code that signal out of order? (Trying to get at the concept here of conditions not having memory.) | 2014-04-16 | I'm not 100% sure I understand what you're getting at here, David - could you elaborate? | |
| 65 | OK | (defprotocol CollReduce should be (defprotocol CollReduce | 2014-04-21 | Thanks Siva, \n \nCan you explain why you think it's the wrong way around a little further? The Clojure source has it the way that's currently used in the book: \n \nhttps://github.com/clojure/clojure/blob/master/src/clj/clojure/core/protocols.clj#L17 \n \nHave I missed something? | |
| 40 | ERROR | In the full Counter.java linked from page 40, the lock is declared local to each Counter class so it doesn’t work as written. To fix the bug, change the declaration to private static ReentrantLock lock; | 2014-04-20 | Man! How did I miss that? Thanks David - much appreciated. \n \n(in case you're wondering, this was a bug introduced when "reformatting" the code to make it look better on the page - unfortunately, I ended up doing rather more than "reformatting"). | |
| 165 | TYPO | “That means that a go block compares very favorably to an Elxir process—a very impressive result given that Clojure runs on the JVM, whereas Elixir runs on the Erlang virtual machine, which was built with efficient concurrency in mind.” “Elixr” should be “Elixir”. | 2014-05-22 | ||
| 28 | TYPO | In the “Joe asks” box on p28, “voilate” should be “violate”. | 2014-05-22 | ||
| 141 | TYPO | Half way down page 141, on the right-hand side, “fault-tolerate” should be “fault-tolerant”. | 2014-05-22 | ||
| 113 | TYPO | First line at the top of page 113: “claim-chopstics!” is missing the “k” in chopsticks. | 2014-06-16 | ||
| 77 | OK | if you get the count of words, do some instructions and then replace the wordcount by count + addedCount you are leaving the door open for other threads to increment the wordcount while you are replacing it with an old incremented wordcount… | 2014-06-26 | Forgive me, Filip, I'm not sure exactly which bit of the book you're talking about here. Page 77 doesn't contain any wordcount code? Are you reporting a bug that you've found in one of the examples? If so, which one? I'd be very grateful if you could provide more context. Thanks! | |
| X | OK | It seems that my e-reader is giving me different pages then you are able to see so I will give more information: I am starting from the following point in Chapter 2; day 3 and will be commenting inline: “Happily, we’re not beaten yet. ConcurrentHashMap in java.util.concurrent looks like exactly what we need. Not only does it provide atomic read-modify-write methods, but it’s been designed to support high levels of concurrent access (via a technique called lock striping). Here’s a modified countWord that uses ConcurrentHashMap:” The following code is being run with many consumers so many threads will be writing to the counts hash map… private void countWord(String word) { ///////////////////// Take a current count for the number… | 2014-06-26 | ||
| 6 | TYPO | In the sentence starting with | 2014-06-25 | ||
| 78 | ERROR | Is: server.core=> (sentence-split “This is a sentence. Is this?! A fragment”) sentence-split is not available in the server.core namespace. | |||
| 104 | ERROR | Java style comment (// Side-effect in transaction - DON’T DO THIS) causes Can be replaced with ; Side-effect in transaction - DON’T DO THIS | |||
| 163 | ERROR | The book doesn’t specify a clojure or core.async version, so I’m using the latest. Dependencies: The following function will no longer compile. The recur must be the last form in the go-loop. I made it work by changing the when-let to an if-let and closing the channel on the “else”. My understanding is that Clojure enforces this as it does not perform TCO when compiling. (defn get-primes [] (let [primes (chan) Thanks, Tim Also, thanks for the book! It’s really helped me understand core.async better. | |||
| 190 | TYPO | There is a broken image on the page in the Pipelining section. I can see the image in the epub version of the book. | |||
| 66 | SUGGEST | Was really enjoying the book till I got to the reducible section, in particular the my-map example. I really think that example is out of context with whats gone before and I found it as hard to follow as any example I can ever think of seeing in a practical programming book. Its unfortunate as reducers seem subtle but also quite interesting, but even after studying the code quite a bit I really can’t work out whats going on. Obviously this will partly be because I’ve never really used Clojure, but even allowing for that I think the example could do with some work. The variable names chosen (reducef, f1, v) don’t help but I also think it might be worth a diagram or some overall explanation of the interactions between the functions here. Ta, Colin | |||
| 80 | SUGGEST | I think a diagram and clear text explanation of the HTTP requests that are going on would definitely help make the final example easier to follow. In addition in this case it might be worth adding an explanation of the “get-translation” function as I think it would be quite easy for the reader to think they’ve understood it without fully following. | |||
| 94 | SUGGEST | For people who are reading this on a tablet or e-reader it might be useful if functions like new-session had a comment explaining what initial is, once you know its a map its much easier for non-Clojure dev to see what’s going on. I know its not the sort of comment you’d want in real code in a dynamic language but I still think it would be useful here. | |||
| 112 | TYPO | I think you mean atom in this bullet point and the one before: “The choice between an STM and an agent-based solution is largely one of | |||
| 99 | ERROR | (send my-agent #((Thread/sleep 2000) (inc %))) is resulting in either an NullPointerException or in no increment with my environment ( changing it to (send my-agent #( do (Thread/sleep 2000) (inc %) ) ) seems to work for me. I am not a Clojure expert but the problematic part seems to be the Thread.sleep(long) method that return void user=> (Thread/sleep 2000) The explicit do seems to be the solution here. | |||
| 104 | TYPO | transfers is an agent and earlier you say calling await waits for all actions from current thread to complete, however in the -main method you have to call transfers even though it is the other 2 threads that interact with it. I thought it might be worth explaining why as it seems interesting. | |||
| 1 | SUGGEST | As well as covering FRP I think it might be worth having a chapter on node.js, mainly because its single-threaded nature make it interesting and impose restrictions that are perhaps worth discussing. | |||
| 35 | SUGGEST | Since the book only requires 100,000 pages from the Wikipedia dump, I’d suggest using the Simple Wikipedia dump [1]. It comes with 200,000 pages, and is only 100MB compressed. This is much easier to set up than the full English version at 10GB compressed. Tim [1]: dumps.wikimedia.org/simplewiki/ | |||
| 58 | SUGGEST | Some Wikipedia dumps return ‘nil’ for certain pages, resulting in null pointer exceptions when ‘get-words’ is called. An easy fix is to have ‘get-words’ check that its input isn’t nil. (defn get-words [text] | |||
| 35 | ERROR | `’luaxlib.h’ file not found` on OSX with new install of lua via homebrew. Tried running cmake with `include_directories(/usr/local/include)` both included and not included in `CMakeLists.txt`. | |||
| 169 | TYPO | In `poll-fn` and `poll`, the local variable `seconds` is mis-named; it’s actually milliseconds. | |||
| 163 | ERROR | I ran into basically the same issue as report #77231. Where go-loop/recur examples are not working. Using the map-chan example I have found that if you switch from using go-loop to (go (loop…) (close! ..)) you can get the code working. (defn map-chan [f from] | |||
| 99 | ERROR | The problem with “#((Thread/sleep 2000) (inc %))” is that Clojure expects a form in “#()”, not a sequence. The “do” at the start of the form passes a sequence of forms, which is why “#(do (Thread/sleep 2000) (inc %))” works. | |||
| 27 | SUGGEST | In the ConcurrentSortedList class you traverse a list backwards when you calculate list size. Is there a particular reason you do it this way? Did you want to show that it will not deadlock? Would it be also ok if we went forward? | |||
| 40 | 26 | ERROR | ConcurrentSortedList example is flawed in my opinion. If a thread is interrupted before the next lock is acquired, “next.lock.unlock();” will throw IllegalMonitorStateException. More serious problem is that if we are interrupted after “next = current.next;” then we never release the next lock, since it points to the next node already. It also doesn’t follow the pattern described when you introduce ReentrantLock’s (unlocks are swapped in your code): I found your explanation why you implemented it this way to keep the code clear, but this version is buggy and should be improved or replaced by another example. | ||
| 40 | ERROR | Might be missing something, but I think that the ConcurrentHashMap example has a race condition. | |||
| 16 | ERROR | Hi, this is a good book for java developers. I write here just want to get a confirm about the right understanding about the sample code. At the end of Chapter 2, about the alien methods, I think the example for defensive copy is incorrect. In fact, the clone operation of listeners only cause a shallow copy for the ArrayList. As a result, listenersCopy and listeners still share the same elements in them, even though they are two separate ArrayLists. So I think it can not guarantee the effect stated below the sample code. If I have any mistake please feel free to correct me. Thanks! | |||
| 45 | TYPO | ‘distributed shared-memory’ should be ‘distributed-memory’. |