Uncle Bob, JSPS: Learning Clojure 124

Posted by Uncle Bob Mon, 20 Jul 2009 02:10:00 GMT

(JSPS: Just Some Poor Schmuck)

I’m trying to learn Clojure, and I’m finding the effort challenging. Perhaps you can help me.

Programming in Clojure (A derivative of Lisp) requires a certain “inside-out” thinking. I’m finding the combination of the shift in thought process and TDD quite difficult to resolve. So I thought I’d ask for your help.

Below is the Bowling Game written in Clojure complete with unit tests. I’m not at all pleased with the result. The tests, especially, look bizzare and backwards to me.

Please post your own solutions to this so that we can all compare and contrast them.

Tests

(ns bowling-game)
(use 'clojure.contrib.test-is)
(use 'bowling-game)

(defn roll-list [game list]
  (if (empty? list)
    game
    (roll-list (roll game (first list)) (rest list))
    ))

(defn roll-many [game n pins]
  (loop [i n g game]
    (if (zero? i)
      g
      (recur (dec i) (roll g pins)))))

(defn gutter-game [game] (roll-many game 20 0))

(deftest can-create-game
  (is (not (nil? (new-game)))))

(deftest gutter-game-should-score-0
  (is (= 0 (score (gutter-game (new-game))))))

(deftest all-ones-should-score-20
  (is (= 20 (score (roll-many (new-game) 20 1)))))

(deftest one-spare
  (is (= 16 (score
    (roll-many
      (roll-list (new-game) [5 5 3])
      17 0)))))

(deftest one_strike
  (is (= 24 (score
    (roll-many
      (roll-list (new-game) [10 3 4])
      16 0)))))

(deftest perfect-game
  (is (= 300 (score (roll-many (new-game) 12 10)))))

(run-tests 'bowling-game)

Code

(ns bowling-game)
(defn new-game [] [])

(defn next-two-balls [rolls]
  (+ (rolls 0) (rolls 1)))

(defn score-strike [rolls]
  [1 (+ 10 (+ (rolls 1) (rolls 2)))])

(defn score-spare [rolls]
  [2 (+ 10 (rolls 2))])

(defn score-no-mark [rolls]
  [2 (next-two-balls rolls)])

(defn score-next-frame [rolls]
  (if (= 10 (first rolls))
    (score-strike rolls)
    (if (= 10 (next-two-balls rolls))
      (score-spare rolls)
      (score-no-mark rolls))))

(defn score [game]
  (loop [frame 1 rolls game score 0]
    (if (> frame 10)
      score
      (let [frame-score (score-next-frame rolls)]
        (recur (inc frame) (subvec rolls (frame-score 0)) (+ score (frame-score 1)))))))

(defn roll [game pins]
  (conj game pins))
Comments

Leave a response

  1. Avatar
    Jason about 2 hours later:

    I think it would probably be more beneficial to focus on clojure tha language and not get hung up on the testing aspect of it at first. You may hate this advise but it will ultimately make more sense in the long run. Functional programming is different and you shouldfocus on that. Testing will follow and make much more sense in time.

  2. Avatar
    Auramo about 4 hours later:

    One thing you could do is use the let construct more in the tests to name your return values you are asserting against.

  3. Avatar
    Auramo about 6 hours later:

    After thinking about my previous comment for a while I realize that my advice on let would just introduce clutter unless you extract the actual contents of the original tests to a separate function. Doing that might be overkill.

    The test-is library IMHO would benefit from xUnit-like functions/macros: is-equal, is-not etc. These are of course trivial to write, but it’s always better to have them in the framework. I quess test-is doesn’t have those because it tries to be as minimal as possible (?)

  4. Avatar
    Chris about 6 hours later:

    I am also having a bit of a challenge w/ Clojure. I will say that the prag prog Programming Clojure book has been a useful assistant. I have not been able to vocalize specific challenge with it but I believe ‘inside out thinking’ is pretty accurate.

    Test first is simply the way I think and I perhaps have a more challenging time expressing the idealized API of the code under test. This could simply be a language newb issue, or it could be something else. It could easily be that I can’t really see the cross programming paradigm fp/oo first principles…

    I think that is likely it; I am having trouble sorting out is precisely what ‘first principles’ are applicable in non-oo languages. These principles are even more difficult to pull out in hybridized oo/ fp languages. In a way I have had an easier time in Haskell than in Scala, F# and Clojure.

    DRY, small, intention revealing, factored – more cross paradigm rules???

  5. Avatar
    Michael Harrison about 11 hours later:

    Bob, it’s great to hear you’re interested in Clojure! I hope you enjoy your journey.

    That “inside-out” feeling you’re having is probably the cause of your unease about the tests you’re writing. Clojure, like Lisps in general, is more about the application of functions to the results of other applications of functions: (roll-list (roll-many (new game).... You may find that in time you get used to these nested parenthetical function applications, and it becomes much faster to parse them mentally. But here are some thoughts on making the test definitions less bizarre.

    You’re doing what I would do in your tests, creating helpful functions like roll-list, roll-many, and gutter-game so actual deftest expressions are as terse as possible.

    You could experiment with doing more “setup” work in the defns, or even just making them value definitions:

    (def gutter-game-score (score (roll-many (new-game) 20 0)))

    (deftest gutter-game-should-score-0
    (is (= 0 gutter-game-score)))

    This might be appropriate if you can’t think of tests that require you to pass an argument other than a new game, and if you’re only focused on scores produced by your code. I think (= 0 gutter-game-score) is a very tidy assertion.

    As Auramo suggested, you can use let to define values in your tests, to make the actual assertion terser:

    (deftest one-spare
    (let [one-spare-score (score (roll-many
    (roll-list (new-game) [5 5 3]) 17 0))]
    (is (= 16 one-spare-score))))

    It’s also likely that test frameworks in Clojure will improve and develop: it’s still early in the game.

    One other tip for you. I noticed you named ‘one-spare’ with a hyphen and ‘one_strike’ with an underscore. Both are legal names, but you’ll save yourself some trouble deciding on one standard. I mixed the two symbols liberally early in my FP days, and misery briefly ensued as I confronted errors about missing functions.

    Happy coding!

  6. Avatar
    http://twitter.com/arwagner about 12 hours later:

    Uncle Bob, I would just make two comments: one, the first two functions in your test code look like classic reduce and map functions, respectively. E.g., something roughly like this: (defn roll-list [game list] (reduce roll (conj game list))) and (defn roll-many [game n pins] (nth (iterate #(roll % pins) game) n))

  7. Avatar
    Dave about 12 hours later:

    Have you considered using Clojure’s -> macro? It lets you sequence together calls in a way which lets you get rid of the deep, back-to-front function call nesting.

    Eg:

    (deftest one-spare
      (is (= 16
        (-> (new-game) (roll-list [5 5 3]) (roll-many 17 0) score))))
    
  8. Avatar
    Micah Martin about 13 hours later:

    A couple guys at 8th Light, including myself, went through this same exercise. My solution is posted at http://blog.8thlight.com/articles/2009/7/20/bowling-in-clojure

    I look forward to learning ways to write better clojure.

  9. Avatar
    Kjetil Valstadsve about 13 hours later:

    Non-OO programming can get “inside-out” if you’re too deeply embedded in OO thinking. Your first warning sign is learning Smalltalk.

    As for first principles, look for the ground rules for ADT’s. In traditional message-sending-model OO such as Smalltalk and Java, ADT’s are types in hierarchies. They get decorated with methods and their instances send each other messages to get stuff done. In Scheme or Lisp, you define some list structure, and create the ADT by writing the functions that interpret it.

    Then there is the multiple-dispatch we find in CLOS, and that we find in Clojure too. Message-sending OO is single-dispatch, meaning that each message has a single receiver, used to dispatch to actual methods. Multiple-dispatch means the method is resolved based on more instances. This step from single to multiple basically inverts the whole way you build abstractions. Instead of just thinking about your instances in a hierarchy, you must think of your methods in a hierarchy as well, and it runs off into multiple dimensions very quickly. This means Lispers have infinitely more potential for messing up, but I think they like to stick to the Lisp/Scheme way outlined above.

    It will be interesting to see which best practices surface in the Clojure community over the next few years, it should be a common ground for many different schools of thought.

  10. Avatar
    Stuart Halloway about 13 hours later:

    Hi Uncle Bob, glad to see you are looking at Clojure. I am working on my solution over at http://github.com/stuarthalloway/clojure-bowling, and I will post in more detail when I am happy with it.

    There’s already some pretty interesting stuff there—most importantly, thinking in terms of a sequence of scores instead of just a total.

  11. Avatar
    Jim Oly about 14 hours later:

    First, I agree with Michael Harrison that the test functions look better if you define values to test and then use them in the test function. I would likely do it this way:

    (def gutter-game (roll-many (new-game) 20 0))
    
    (deftest gutter-game-should-score-0
      (is (= 0 (score gutter-game))))
    

    This can be done with the other game tests as well, and makes it easier if you want to test different functions over the same game.

    roll-many would benefit from the built-in repeat or replicate functions:
    (defn (defn roll-many [game n pins]
      (roll-list game (replicate n pins)))
    
    reduce was mentioned earlier as a way to build up a result from a collection. I used it to simplify roll-list, since each call to roll takes a game and a move to make a new game. It’s much nicer than a manual loop and recur when applicable.
    (defn roll-list [game list]
      (reduce roll game list))
    

    In fact, I would be tempted to add this functionality into new-game. The zero argument case would return the empty vector as normal, and the single argument case would act as roll-list on a new game:

    (declare roll)
    (defn new-game
      ([] [])
      ([rs] (reduce roll [] rs)))
    

    This would subsume some roll-list calls, though roll-list would still be needed for defining the single spare and strike games. You could create the game from any sequence, so both (new-game '(1 2 3)) and (new-game [1 2 3]) would work.

    (Note: the initial declare is needed since roll is defined later. Moving roll earlier would eliminate the need, but having new-game precede it seems nicer.)

    I’m still working on the main body of the code, but here are a few other changes I like. In score, instead of using let to hold the result of a score-next-frame call and pick out the pieces later, we can use destructuring to directly pull out and name them. frame-score becomes [rolls-used single-score], and we can use rolls-used and single-score instead of (frame-score 0), etc. Here’s my resulting score:

    (defn score [game]
      (loop [frame 1 rolls game score 0]
        (if (> frame 10)
          score
          (let [[rolls-used single-score] (score-next-frame rolls)]
            (recur (inc frame) (subvec rolls rolls-used) (+ score single-score))))))
    

    And lastly, I like the style of using cond for multiple condition statements, so I replaced the nested ifs in score-next-frame. I think this is up to the individual programmer, but having all the cases lined up looks nicer to me.

    (defn score-next-frame [rolls]
      (cond (= 10 (first rolls)) (score-strike rolls)
            (= 10 (next-two-balls rolls)) (score-spare rolls)
            :else (score-no-mark rolls)))
    
  12. Avatar
    Stuart Halloway about 14 hours later:

    Ok, I am up to a solution I like reasonably well at http://github.com/stuarthalloway/clojure-bowling. Here’s the key bits:

    
    (ns bowling-game
      (:use clojure.contrib.seq-utils))
    
    (defn strike? [rolls]
      (= 10 (first rolls)))
    
    (defn spare? [rolls]
      (= 10 (apply + (take 2 rolls))))
    
    (defn balls-to-score
      "How many balls contribute to this frame's score?" 
      [rolls]
      (cond
       (strike? rolls) 3
       (spare? rolls) 3
       :else 2))
    
    (defn frame-advance
      "How many rolls should be consumed to advance to the next frame?" 
      [rolls]
      (if (strike? rolls) 1 2))
    
    (defn frames
      "Converts a sequence of rolls to a sequence of frames" 
      [rolls]
      (if rolls
        (lazy-seq (cons (take (balls-to-score rolls) rolls)
                        (frames (drop (frame-advance rolls) rolls))))))
    
    (defn score
      "Score a bowling game, passed as a sequence of rolls." 
      [rolls]
      (apply + (flatten (take 10 (frames rolls)))))    
    
    (ns test.bowling-game
      (:use clojure.test)
      (:use bowling-game))
    
    ; Tests could be improved by a macro that captures the descriptions,
    ; or by modifying 'are' to do so in some way.
    
    (deftest test-balls-to-score
      (are [description balls frames] (= balls (balls-to-score frames))
           "strike" 3 [10 10 10]
           "spare" 3 [5 5 10]
           "no mark" 2 [5 3 5]))
    
    (deftest test-frame-advance
      (are [description advance frames] (= advance (frame-advance frames))
           "strike" 1 [10]
           "spare" 2 [5 5]
           "no mark" 2 [5 4]))
    
    (deftest test-frames-for-various-games
      (are [description expected-frames game] (= expected-frames (take 10 (frames game)))
           "gutter game" (repeat 10 [0 0]) (repeat 0)
           "all ones" (repeat 10 [1 1]) (repeat 1)
           "all fives (spares)" (repeat 10 [5 5 5]) (repeat 5)
           "all tens (strikes)" (repeat 10 [10 10 10]) (repeat 10)
           ))
    
    (deftest test-various-games
      (are [description expected-score game] (= expected-score (score game))
           "gutter game" 0 (repeat 0)
           "all ones" 20 (repeat 1)
           "one spare" 16 (concat [5 5 3] (repeat 0))
           "one strike" 24 (concat [10 3 4] (repeat 0))
           "perfect game" 300 (repeat 10)
           ))
    
    
  13. Avatar
    Phil about 15 hours later:

    Any chance you could syntax-highlight your code? My brain simply shuts down when I try to read monochrome code.

  14. Avatar
    Jim Oly about 15 hours later:

    I like Stuart’s approach. I was keeping the game in a vector as the original code did because it most easily supported the requirements for the roll function. It was causing problems in my attempts at simplifying the main body of code though. Switching over to sequences makes a lot of sense for score.

    Out of curiosity, what is the idiomatic way of adding a single element to the end of a sequence? That would make it easy to implement roll and use sequences everywhere.

  15. Avatar
    Mark Engelberg about 18 hours later:

    Just use recursion, and the code writes itself…

    
    (use 'clojure.contrib.test-is)
    
    (defn sum [s] (reduce + s))
    (defn score [game]
     (cond
       (< (count game) 3)          (sum game),
       (= (first game) 10)         (+ (sum (take 3 game)) (score (drop 1 game))),
       (= (sum (take 2 game)) 10)  (+ (sum (take 3 game)) (score (drop 2 game))),
       :else                       (+ (sum (take 2 game)) (score (drop 2 game)))))
    
    (deftest sample-games
     (is (score [1 0 1 10 2 2 10 3 3 10 1 10 3 10 10 1 2]) 108)
     (is (score [1 0 1 10 2 2 10 3 3 10 1 10 3 10 1 10 10 8 0]) 121)
     (is (score [1 0 1 10 2 2 10 3 3 10 1 10 3 10 1 10 8 10 9]) 120))
    
    

    You can improve efficiency by enforcing an input of vectors and using subvec, or you can keep the generality of sequences but change the first test to something like (= (count (take game 3)) 3), but really, why bother doing anything at the expense of clarity for a problem where the inputs are so small that efficiency is a non-issue.

  16. Avatar
    Mark Engelberg about 18 hours later:

    BTW, the best book for learning how to think recursively is How To Design Programs, available for free at htdp.org.

  17. Avatar
    Mark Engelberg about 19 hours later:

    Whoops, I guess I don’t understand bowling scoring as well as I thought. Now that I’ve read up a bit more on bowling scoring, I see that if you get down to three rolls, (say 10, 7, 2) it must be scored differently depending on whether it is a strike in the 9th frame followed by 2 balls in the 10th frame or just three balls from the 10th frame (in the first scenario, the second and third ball get counted twice). So it looks like you really do need to assemble it into a list of frame-by-frame scores.

    Unfortunately, I misused the “is” macro (forgot to put =), which prevented me from catching my incorrect tests.

    The result is similar in spirit to Stuart’s code, but a bit more compact without as many helper functions.

    
    (use 'clojure.contrib.test-is)
    
    ; A game is a sequence of numbers, representing how many pins were knocked down for that roll
    
    (defn sum [s] (reduce + s))
    (defn frame-scores [game]
      (cond
        (< (count game) 3)          [(sum game)],
        (= (first game) 10)         (cons (sum (take 3 game)) (frame-scores (drop 1 game))),
        (= (sum (take 2 game)) 10)  (cons (sum (take 3 game)) (frame-scores (drop 2 game))),
        :else                       (cons (sum (take 2 game)) (frame-scores (drop 2 game)))))
    (defn score [games] (sum (take 10 (frame-scores games))))
    (deftest sample-games
      (is (= (score [6 1 9 0 8 2 5 5 8 0 6 2 9 1 7 2 8 2 9 1 7]) 127))
      (is (= (score [10 10 7 3 8 2 10 9 1 10 10 10 10 7 3]) 232))
      (is (= (score [10 10 7 3 8 2 10 9 1 10 10 10 7 2]) 210))
      (is (= (score [5 5 8 2 9 1 7 3 8 2 6 4 9 1 7 3 6 4 4 5]) 163))
      (is (= (score [10 10 10 10 10 10 10 10 10 10 10 10]) 300)))
    
    
  18. Avatar
    Mark Engelberg about 19 hours later:

    Sorry for the confusing choice of variable name. Should be “game” not “games” in:

    (defn score [game] (sum (take 10 (frame-scores game))))
  19. Avatar
    Stuart Halloway about 24 hours later:

    While Mark’s solution is cool (and very short) I believe that decomposing into named helper functions better blends TDD and functional style. I have written up my approach over on the RunCodeRun blog.

  20. Avatar
    Mark Triggs 1 day later:

    Another variation on Stuart’s approach here, but modifying slightly to make the notion of “types of rolls” into a first-class concept:

    
    (ns bowling-game
      (:use clojure.contrib.test-is
            clojure.contrib.seq-utils))
    
    (defn sum [xs] (apply + xs))
    
    (def *roll-types*
         [{:name "regular (underachieving?)" 
           :satisfies #(< (sum (take 2 %))
                          10)
           :consumes 2
           :advances 2}
    
          {:name "strike" 
           :satisfies #(= (first %) 10)
           :consumes 3
           :advances 1}
    
          {:name "spare" 
           :satisfies #(= (sum (take 2 %))
                          10)
           :consumes 3
           :advances 2}])
    
    (defn roll-type [rolls]
      (find-first #((:satisfies %) rolls)
                  *roll-types*))
    
    (defn frames [rolls]
      (when (seq rolls)
        (let [{:keys [consumes advances]} (roll-type rolls)]
          (cons (take consumes rolls)
                (frames (drop advances rolls))))))
    
    (defn score-game [rolls]
      (sum (map sum
                (take 10 (frames rolls)))))
    
    ;; tests
    (deftest test-score-game
      (is (= (score-game [1 5 3 6 7 2 3 6 4 4 5 3 3 3 4 5 8 1 2 6])
             81)))
    
    (deftest test-score-strike-game
      (is (= (score-game [10 3 6 7 2 3 6 4 4 5 3 3 3 4 5 8 1 2 6])
             94)))
    
    (deftest test-score-spare-game
      (is (= (score-game [1 9 3 6 7 2 3 6 4 4 5 3 3 3 4 5 8 1 2 6])
             88)))
    
    (deftest test-strike-then-spare
      (is (= (score-game [10 4 6 7 2 3 6 4 4 5 3 3 3 4 5 8 1 2 6])
             103)))
    
    (deftest test-two-strikes
      (is (= (score-game [10 10 7 2 3 6 4 4 5 3 3 3 4 5 8 1 2 6])
             112)))
    
    (deftest test-two-spares
      (is (= (score-game [8 2 5 5 7 2 3 6 4 4 5 3 3 3 4 5 8 1 2 6])
             98)))
    
    (deftest test-last-spare
      (is (= (score-game [1 5 3 6 7 2 3 6 4 4 5 3 3 3 4 5 8 1 2 8 7])
             90)))
    
    (deftest test-last-strike
      (is (= (score-game [1 5 3 6 7 2 3 6 4 4 5 3 3 3 4 5 8 1 10 7 2])
             92)))
    
    (deftest test-last-spare-then-strike
      (is (= (score-game [1 5 3 6 7 2 3 6 4 4 5 3 3 3 4 5 8 1 2 8 10])
             93)))
    
    (deftest test-perfect-game
      (is (= (score-game [10 10 10 10 10 10 10 10 10 10 10 10])
             300)))
    
    (deftest test-whole-game
      (is (= (score-game [6 3 7 1 8 2 7 2 10 6 2 7 3 10 8 0 7 3 10])
             135)))
    
  21. Avatar
    Dan Pierkowski 1 day later:

    A caveat:The guy I worked with on this and I are from Boston, so we scored this according to candle-pin rules. That may make this less useful for some folks.

    A buddy and I were talking through designs for this, and as we talked about scoring frames, we realized that there were three cases:

    1) Strike – score the first ball, plus the next two ‘bonus’ balls, then remove the first ball (the strike) from the list, then continue scoring.

    2) Spare – score the first two balls, plus the next ‘bonus’ ball, then remove the first two balls (the spare) from the list, then continue scoring.

    3) No-mark – score all three balls in the frame, then remove them from the list, then continue scoring. (Remember: candlepin!)

    In all these cases you want to score three balls, the only difference was in how many balls you remove from the list before you continue scoring (i.e., how many balls were in the frame you are removing from the yet-to-be-scored list). So this is what we came up with:

    http://github.com/zdsbs/candlepin-bowling/tree/master

    (ns scorecard)
    
    (defn third [rolls]
        (first (next (next rolls))))
    
    (defn first-two [rolls]
        (+ (first rolls) (second rolls)))
    
    (defn strike? [rolls]
        (= 10 (first rolls)))
    
    (defn spare? [rolls]
        (= 10 (first-two rolls)))
    
    (defn remove-strike [rolls]
        (rest rolls))
    
    (defn remove-spare [rolls]
        (rest (rest rolls)))
    
    (defn remove-normal-frame [rolls]
        (rest (rest (rest rolls))))
    
    (defn remove-frame [rolls]
            (if (strike? rolls)
                (remove-strike rolls)
                (if (spare? rolls)
                    (remove-spare rolls)
                    (remove-normal-frame rolls))))
    
    (defn score [input-rolls]
        (loop [rolls input-rolls score 0 frame-counter 0]
            (if (or (empty? rolls) (= 10 frame-counter))
                score
                (recur 
                    (remove-frame rolls) 
                    (+ score (first rolls) (second rolls) (third rolls)) (inc frame-counter)))))
    

    And the tests:

    (ns scorecard-test
        (:use clojure.contrib.test-is 
            scorecard))
    
    (deftest test-third
        (is (= 3 (third [1 2 3 4]))))
    
    (deftest test-first-two
        (is (= 3 (first-two [1 2 3]))))
    
    (deftest test-remove-frame
        (is (= [3 4 5] (remove-frame [0 1 2 3 4 5])))
        (is (= [3 4] (remove-frame [10 3 4])))
        (is (= [5] (remove-frame [6 4 5]))))
    
    (deftest test-remvo-two-frames
        (is (= [] 
            (remove-frame 
                (remove-frame [0 1 2 0 0 0])))))
    
    (deftest test-scores
        (is (= 0 (score [])))
        (is (= 6 (score [1 2 3])))
        (is (= 15 (score [1 2 3 4 5 0])))
        (is (= 19 (score [10 1 2 3])))
        (is (= 17 (score [5 5 1 2 3])))
        (is (= 300 (score [10 10 10 10 10 10 10 10 10 10 10 10])))
        (is (= 19 (score [5 5 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 ])))
        (is (= 21 (score [10 1 1 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 ]))))
    
  22. Avatar
    Uncle Roastie 4 days later:

    Uncle Bob, we met several times at Xerox – you taught me the real intent of OOD/OOP. I’ve since moved on to C#, and some guys I work with decided to take a functional language course last fall to get mentally into modern days. The end result: you must think bottom-up for FP – get rid of the top-down mindset (for FP). Small-small functions (the mechanism code), no nested ifs, and your higher level functions (the policy code) just call the lower-level ones. Needing some closures is common. Map your tests to the functions after you are done. Once you are comfortable with the mindset, then you can start writing your tests first. Get comfortable with map/filter/reduce.

  23. Avatar
    moncler clearance 9 months later:

    Very quietly I take my leave.To seek a dream in http://www.edhardy-buy.com/ starlight.

  24. Avatar
    FLV to ipad converter 9 months later:

    yeah aj

  25. Avatar
    http://linkbuilding1000k.com 10 months later:

    They should feel satisfied with the output. Otherwise you can not subumit feedback and get positive response. Good luck for all!

  26. Avatar
    Carpet Cleaner Raleigh 10 months later:

    Thank you very much for the post. I will bookmark this and I want to know still more information regarding this blog. Thanks for sharing.

  27. Avatar
    hair salon software 11 months later:

    I think it would probably be more beneficial to focus on clojure tha language and not get hung up on the testing aspect of it at first. Thanks for sharing.

  28. Avatar
    jewellery 11 months later:

    I like This site! Thank you for your information

  29. Avatar
    tiffany 11 months later:

    They should feel satisfied with the output. Otherwise you can not subumit feedback and get positive response. Good luck for all!

  30. Avatar
    basketball jerseys 11 months later:

    best

  31. Avatar
    Wire mesh 12 months later:

    I found a lot of great points in this post, nice

  32. Avatar
    cheap vps about 1 year later:

    hat “inside-out” feeling you’re having is probably the cause of your unease about the tests you’re writing. Clojure, like Lisps in general, is more about the application of functions to the results of other applications of functions: (roll-list (roll-many (new game).... You may find that in time you get used to these nested parenthetical function applications, and it becomes much faster to parse them mentally. But here are some thoughts on making the test definitions less bizarre. cheap VPS You’re doing what I would do in your tests, creating helpful functions like roll-list, roll-many, and gutter-game so actual deftest expressions are as terse as possible.

    You could experiment with doing more “setup” work in the defns, or even just making them value definition

  33. Avatar
    ricky about 1 year later:

    Clojure, like Lisps in general, is more about the application of functions to the results of other applications of functions: (roll-list (roll-many (new game).... You may find that in time you get used to these nested parenthetical function applications, and it becomes much faster to parse them mentally. But here are some thoughts on making the test definitions less bizarre. cheap VPS You’re doing. seo

  34. Avatar
    http://www.shopofbrides.com about 1 year later:

    They should feel satisfied with the output. Otherwise you can not subumit feedback and get positive response. Good luck for all!

  35. Avatar
    http://www.shopofbrides.com about 1 year later:

    really good, nice .Very informative and trustworthy blog. Please keep updating with great posts like this one. I have booked marked your site and am about to email it to a few friends of mine that I know would enjoy readingwedding dresses

  36. Avatar
    flower philippine valentine about 1 year later:

    I would never find a better place to read as good comments as this site never seen before.Its easy to find easy to understand, and it have serious comments not sick jokes as others, thanks and please keep like this.

  37. Avatar
    Rangemaster 110 about 1 year later:

    I don’t understand that clojure things

  38. Avatar
    steelpipeni about 1 year later:
    bestsage
  39. Avatar
    Designer Bags about 1 year later:

    Cool!Thanks for ur nice sharing!!It help me a lot with those information!

  40. Avatar
    Women UGG about 1 year later:

    Women UGG Thanx for sharing this with all of us. Of course, what a great site and informative posts, I will bookmark this site.

  41. Avatar
    how can i send a text message from my computer about 1 year later:

    nice blog,but you could have made this blog more cool if you could configure the language changer pluigin but all the same you have done a good job

  42. Avatar
    Pandora about 1 year later:

    it probably doesn’t deserve all of the build up that I just gave it, but I suspect that I’ll be writing more about this topic. There’s definitely more to discuss.

  43. Avatar
    High Protein Low Fat Foods about 1 year later:

    You totally expressed the same thoughts that I was thinking with this wonderful blog post. Please keep readers

    such as myself engaged and keep producing such great content.

  44. Avatar
    puma about 1 year later:

    If you mean to find great shoes for your children puma speed trainers also provides a mixture of finicky and affordable shoes for them. There are a lot of choices, it is up ring call,Ugg Boots, after by people that indigence an incredible quantity of column. This will make the customers happier. If you are often tangled in Singapore womens puma future cat shoes sale at Sainte Marie that could enhance operational efficiency, range visibility and turnaround time,” said Desmond Chan, managing boss, South Asia, Menlo Worldwide Logistics. “Our multi-client facility in Boon Lay Way provides puma trainers with different flag. puma uk’s youngest targets are toddlers. The puma for sale shoes are incredibly affordable, yet they still hold the grace. Wearing comfortable shoes will help children exploit better.

  45. Avatar
    http://www.blacktowhiteiphone4.com about 1 year later:

    I just heard the gossip that since there’s some technic problems on white iphone 4, iphone 4b is going to release. It that true?

  46. Avatar
    link of london about 1 year later:

    I thought it was going to be some http://www.davidyurmanclub.com/12-david-yurman-men-necklace-and-chains> david necklaceboring old post, but it really compensated for my time. Links of London necklace I will post a link to this page on my blog. I am sure my visitors will find Links of London baby productsthat very useful.

  47. Avatar
    High Protein Foods about 1 year later:

    vious comment for a while I realize that my advice on let would just introduce clutter unless you extract the actual contents of the original tests to a separate function. Doing that might be ov

  48. Avatar
    replica watch rado about 1 year later:

    vious comment for a while I realize that my advice on let would just introduce clutter unless you extract the actual contents of the original tests to a separate function. Doing that might be ov

  49. Avatar
    luky about 1 year later:

    looking good, thanks.

  50. Avatar
    iPad PDF Transfer for Mac about 1 year later:

    I really like this essay. Thank you for writing it so seriously. I want to recommend it for my friends strongly. iPad PDF Transfer for Mac can help you transfer ebooks in PDF format from ipad to mac/iTunes.

  51. Avatar
    Criminal check about 1 year later:

    Thanks for this sample code.

  52. Avatar
    cordyceps about 1 year later:

    look good, thank you

  53. Avatar
    Tenant Screening about 1 year later:

    These are of course trivial to write, but it’s always better to have them in the framework. I quess test-is doesn’t have those because it tries to be as minimal as possible.

  54. Avatar
    Criminal Records about 1 year later:

    You’re doing what I would do in your tests, creating helpful functions like roll-list, roll-many, and gutter-game so actual deftest expressions are as terse as possible.

  55. Avatar
    mosic about 1 year later:

    11

  56. Avatar
    cable ties about 1 year later:

    Excellent work in this post.

  57. Avatar
    Designer Sunglasses about 1 year later:

    Buy $10 Replica Designer Sunglasses with 3-day FREE SHIPPING

  58. Avatar
    Apartments for rent Bucharest about 1 year later:

    Learning Clojure is a perfect chance to get a bit of a grasp on the Java world as well. I have never programed in Java a day in my life and I’m learning Clojure and loving every minute of it.

  59. Avatar
    Flannel Sheet Sets about 1 year later:

    Clojure is a dynamic programming language that targets the Java Virtual Machine. It is designed to be a general-purpose language, combining the approachability and interactive development of a scripting language with an efficient and robust infrastructure for multithreaded programming. Thanks.

  60. Avatar
    Bags about 1 year later:

    Whatever style of Bags you are seeking, or even if you are looking to find inspiration to try something new, we have all the latest, innovative, functional, contemporary and classic Juicy couture bags to ensure that we fulfil every requirement you may have when looking for the perfect Bags this season or beyond.

  61. Avatar
    brand name handbags shop online about 1 year later:

    juich handbag is one of the most favour brands.It is an unmistakable sign of sophistication, social status and recognition. In a word ,it is the king of the brands in the fashion world. For me, a LV handbag is not just a handbag, it is the ultimate fashion statement and it represents a dream must come true.
     
    However, an original juicy handbag might simply be very expensive which far way from we can afford. All your desire for the bag may well be dampened when you read the hefty price tag dangling to the original. But you can find a good way to make your dream come ture with an attractive price, beautiful design …
     
    You can typed “LV” in google search engine. And you can find thousand of store online which are all selling cheap LV handbags.High quality Louis Vuitton replica handbags come in every size, shape and styles imaginable and they look like the real thing – almost. High quality replicas are usually made in the same style, using the same kinds of materials and even stitched in the same way as the original. “A+” replicas are really quite as good as the originals.Louis Vuitton replica handbags are the rage among women, for a number of reasons. The cost factor obviously comes first. Genuine juicy handbag costs anywhere between several hundred and a whopping $1,000 or above. Really a bit too much for a handbag, don’t you think? On the other hand, high quality Louis Vuitton replica handbags cost only a fraction of that – approximately $100-$200. So, by buying a replica you can easily purchase several handbags for the cost of a single original.

  62. Avatar
    Yalova Emlak about 1 year later:

    Thanks for this article.I like its.As to me it’s good job.I wait ur next articles and I will read ur new articles.

  63. Avatar
    dory about 1 year later:

    interesting thanks for sharing. Social Network

  64. Avatar
    tiffany and co outlet about 1 year later:

    A bad beginning makes a bad ending. A bird in the hand is worth than two in the bush. A year’s plan starts with

    spring. Bad news has wings. Better to ask the way than go astray. By reading we enrich the mind, by conversation we

    polish it. Custom makes all things easy. He is not fit to command others that cannot command himself. He laughs best

    who laughs last. tiffany and co outlet

  65. Avatar
    Seobaglyak about 1 year later:

    a Seobaglyak igazán éjszaka érzik elemükben magukat. Szinte ”kétlaki” életet élnek…

  66. Avatar
    Google about 1 year later:

    welcom to my blog Wood Pellets Fuel

  67. Avatar
    okey oyunu oyna about 1 year later:

    great code.

    internette görüntülü olarak okey oyunu oyna, gerçek kisilerle tanis, turnuva heyecanini yasa.

  68. Avatar
    leveling kit ford about 1 year later:

    I always read your article Thank you :-)

  69. Avatar
    leveling kit f250 about 1 year later:

    Very cool and I love your post Thanks :-)

  70. Avatar
    f350 leveling kit about 1 year later:

    I want to read more article from your site. thanks :-)

  71. Avatar
    cccccc about 1 year later:

    fdsfdsfdsfdsf

  72. Avatar
    christian louboutin shoes on sale about 1 year later:

    What a wonderful article!

  73. Avatar
    beats by dr dre headphones about 1 year later:

    What a wonderful article!

  74. Avatar
    Jewellery about 1 year later:

    Online UK costume and fashion jewellery shop with,

  75. Avatar
    Jewellery about 1 year later:

    ok baby

  76. Avatar
    boots side zip about 1 year later:

    Online UK costume and fashion jewellery shop with boots side zip

  77. Avatar
    graham Texas about 1 year later:

    Dude!!! I am not much into reading, but somehow I got to read lots of articles on your blog. It’s amazing how interesting it is for me to visit you very often.

  78. Avatar
    rear projection screen about 1 year later:

    Excellent work in this post.Thank

  79. Avatar
    commission predators about 1 year later:

    It’s amazing how interesting it is for me to visit you very often.

  80. Avatar
    wedding dresses online shop over 2 years later:

    Catherine Lash, founder and creative director of The Wedding Co., which organizes the Wedding Show in Toronto, said the beauty of buy wedding dresses online wedding dresses online shop buy dresses onlineroyal weddings is that they are so steeped in tradition. She believes

  81. Avatar
    adres zoeken op naam over 2 years later:

    Very interesting comments! Keep up the good job!

  82. Avatar
    http://www.nlpresults.com/ over 2 years later:

    I am looking forward to reading more informative articles like this. Keep posting! http://www.nlpresults.com/

  83. Avatar
    anthony robbins over 2 years later:

    I am looking forward to reading more informative articles like this. Keep posting! http://www.nlpresults.com/

  84. Avatar
    Bowtrol over 2 years later:

    hmm ,i’m not sure if this is what i’m looking for but anyway this is interresting and could be useful some day,thanks for taking time to write such cool stuff

  85. Avatar
    Crystal Jewellery over 2 years later:

    Great post! Nice and informative, I really enjoyed reading it and will certainly share this post with my friends . Read everything you ever wanted to know about how to buy gold jewelry

  86. Avatar
    beats by dre store over 2 years later:

    s what i’m looking for but anyway this is interresting and could be useful some day,thanks for taking time to write such cool stuf

  87. Avatar
    beats by dre store over 2 years later:

    what i’m looking for but anyway this is interresting and could be useful some day,thanks for taking time to write such cool stufbeats by dre sale cheap beats by dre

  88. Avatar
    Cheap Comics over 2 years later:

    Thank you for sharing such an article.

  89. Avatar
    Clarence Parker over 2 years later:

    The article is wonderfully written and the way the points were sent across is very understandable. I loved it.

  90. Avatar
    Japanese Flower Arranging over 2 years later:

    Thank you for sharing such an article. Anyways, you can check out my site to learn more

  91. Avatar
    bagsupplyer over 2 years later:

    Thanks for share with us.I look forward to reading more. Designer women Lacoste backpacks freee shipping for wholesale

  92. Avatar
    games puzzle over 2 years later:

    thank you for share with us

  93. Avatar
    Lice Pictures over 2 years later:

    very interesting site.thank you so much!i love it!

  94. Avatar
    Flowers For Men over 2 years later:

    Your blog is informative and brilliant. Keep it up!

  95. Avatar
    Mental Disorders List over 2 years later:

    The article is wonderfully written and the way the points were sent across is very understandable. I loved it.

  96. Avatar
    China-Hotels-Accommodation over 2 years later:

    This is a well-written and informative post. China Hotels Accomodation Keep it up!

  97. Avatar
    DR OZ african Mango over 2 years later:

    It is not hard to understand why the question of value is still raised when you consider that today?s outsourcing models have some inherent limitations that reduce the overall gains companies can achieve .

  98. Avatar
    Men’s Formal Shirts over 2 years later:

    A very interesting post.I really appreciate you sharing such article.keep it up

  99. Avatar
    Nail Fungus Pictures over 2 years later:

    I liked how the thoughts and the insights of this article is well put together and well-written. Hope to see more of this soon.

  100. Avatar
    anji over 2 years later:

    Thanks a lot for providing the great info is visible in this blog that to using the great technology in this blog. I am very much satisfied by the great technology in this website that to using the great technology in this website Executive Director Job Description| General Manager Job Description| Consultant Job Description| Webmaster Job Description

  101. Avatar
    Ashley Bowling over 2 years later:

    A new broom sweeps clean A nod’s as good as a wink to a blind horse A penny saved is a penny earned

  102. Avatar
    chy over 2 years later:

    Burberry Outlet September big AD movie has just been published, they cooperates with models Amber Anderson, Matthew Whitehouse, Edie Campbell and Rob Pryor. The model Matthew Whitehouse appears in Burberry AD movie for the second time. The theme is Burberry Nude Color, which derived from the sexy elements of Burberry New Arrival Women Perfume in the nice 1960s.in Burberry UK Prorsum September AD movie, men and women models wear in nude color together, it seems that nude element will become the new fashion focus in this year. The nude color lambs coats wore by models, looks so elegant and exquisite. Burberry offers platform for designers to show literary or artistic talent as always, and combine itself with British artists, weather and music.This season, Burberry Nude Collection is iconic Women Capsule Collection, the clothing are?Burberry Sale , the other kinds include Burberry Sunglasses, Burberry Watches, Burberry Bags, Burberry Shoes. The materials includes satins, sateen, silks, cashmere, lace, PU leather, fur, lambs and so on.

  103. Avatar
    christian louboutin over 2 years later:

    The professional design make you foot more comfortable. Even more tantalizing,this pattern make your legs look as long as you can,it will make you looked more attractive.Moveover,it has reasonable price.If you are a popular woman,do not miss it.

    Technical details of Christian Louboutin Velours Scrunch Suede Boots Coffee:

    Color: Coffee
    Material: Suede
    4(100mm) heel
    Signature red sole x

    Fashion, delicate, luxurious Christian louboutins shoes on sale, one of its series is Christian Louboutin Tall Boots, is urbanism collocation. This Christian louboutins shoes design makes people new and refreshing. Red soles shoes is personality, your charm will be wonderful performance.

  104. Avatar
    beats by dr dre over 2 years later:

    A university studentbeats by dr dre caught by the enemy, the enemy tied him at the poles,just beats solo headphones purple and then asked him: say, where are you? You do not say it electrocuted! Scheap dr.dre beats studio headphones balck/yellowtudents back to the enemy a word, the result was electrocuted, he said: I am TVU.Hot sale beats by dr dre pro headphones

  105. Avatar
    lovepandorabead over 2 years later:

    The pandora halloween bracelet&pandora look alike charm bracelets upgrades you the new queen bee as well as a real princess. Ladies are jealous of you on the one particular hand but can’t assistance turning back again to value you from the bottom of their heart on the alternative hand. They envy that you simply steal the display; they adore people for you personally are their goddess from the existing trend. The first-rate claret is overshadowed as well as the cheap pandora style beads/a> turns into tasteless meals in the grand get together. Nobody can distinguish it is Cartier luxuries you otherwise you flatters the well-known brand.

  106. Avatar
    Blu-ray to H264 over 2 years later:

    What’s more, this powerful Blu-ray video to H.264 converter allows you to convert Blu-ray files in batches, preview and take snapshot of your Blu-ray movie. Blu-ray naar H264

    href=”http://www.bluraytoh264.com/jp.htm”>???H264

    Blu-ray to H.264 Blu-ray ? H264

  107. Avatar
    salsle over 2 years later:

    Christian Louboutin Peep-Toe Very Prive Blue Satin Pumps,http://www.bestclsales.com/christian-louboutin-peep-toe-very-prive-blue-satin-pumps-discount-p-9131.html Christian Louboutin Pigalle 100 Leopard-Print Python Pumps,http://www.brandhotsales.com/christian-louboutin-pigalle-100-leopard-print-python-pumps-p-6654.html Christian Louboutin Pigalle 120 Pointed Toe Pumps Mimosa,http://www.shoes80off.com/christian-louboutin-pigalle-120-pointed-toe-pumps-mimosa-p-6656.html Christian Louboutin Pigalle 120 Pointed Toe Pumps Jade,http://www.brandhotsales.com/christian-louboutin-pigalle-120-pointed-toe-pumps-jade-p-6656.html Christian Louboutin Pigalle Fushsia Glitter Pumps,http://www.brandhotsales.com/ Christian Louboutin Pigalle Pumps Silver,http://www.sergiorossideal.com/christian-louboutin-pigalle-pumps-silver-p-115.html

  108. Avatar
    Modeling News over 2 years later:

    Really impressed! Everything is very, very clear and open. You have shared a lot of valuable information.

  109. Avatar
    Impressive Resume over 2 years later:

    What a unique site the comment was so fun to read and so informative.To know more about the best impressive resume to have a great job just visit my site.

  110. Avatar
    gianmarco lorenzi over 2 years later:

    or span, to cross you must

  111. Avatar
    Womens Moncler Jackets over 2 years later:

    Everyone should have a Mens Moncler Jackets as it is the best outwear against the cold season and is also comfortable and light to wear.

  112. Avatar
    iPhone contacts backup over 2 years later:

    How many people understand this? it is a good choice to make a copy of it and save it on computer. We can retrieve them when necessary.

  113. Avatar
    Discount Louboutin Shoes over 2 years later:

    Every women always has Christian Louboutins Wedding Shoes turn of fame but it also has its own goodbyes.

  114. Avatar
    Polo Ralph Lauren Pas Cher over 2 years later:

    Thanks for this beautiful website. I have enjoyed reading through a few of the articles.

  115. Avatar
    creatine over 3 years later:

    I chose to bookmark this site so I can come back and read it again. It’s extremely well-written and it draws you into the information provided. This is excellent writing.

  116. Avatar
    mbtshoe over 3 years later:

    Australia Beats By Dre Studio dr dre beats headphones beats studio beats pro beats solo hd pro headphones music Official store Monster Beats By Dre Pro

  117. Avatar
    Online Shopping over 3 years later:

    Thanks for the information, I’ll visit the site again to get update information online shopping

  118. Avatar
    louboutin sales over 3 years later:

    Uncle Bob, JSPS: Learning Clojure 117 hoo,good article!!I like the post!152

  119. Avatar
    ptc over 3 years later:

    I like your articles very much.you have a quite exceptional view.Thanks for being here and being so awesome! Don`t stop, keep on.So finally This article is very good. I got many unknown information from this article. Thanks for this great article.

    <”http://www.clixsense.com/”>ptc<”http://www.clixsense.com>

  120. Avatar
    http://www.taiwanmoldmaker.com/ over 3 years later:

    Intertech Machinery Inc. provides the most precise Plastic Injection Mold and Rubber Molds from Taiwan. With applying excellent unscrewing device in molds,

    Intertech is also very professional for making flip top Cap Molds in the world. Mold making is the core business of Intertech (Taiwan). With world level technology, Intertech enjoys a very good reputation for making Injection Mold and Plastic Molds for their worldwide customers.

  121. Avatar
    parchemin over 3 years later:

    Timi and Leslie have totally revitalized their entire line of diaper bags to focus on their mission; provide gorgeous functional luxury diaper bags that appeal to the woman in every mom?

  122. Avatar
    Silicone Molding over 3 years later:

    With more than 20 years of experience, Intertech provides an extensive integrated operational ability from design to production of molds 100% made in Taiwan. Additional to our own mold making factory, we also cooperate with our team vendors to form a very strong working force in Taiwan.

    For the overseas market, we work very closely with local representatives in order to take care of the technical communication and after-sales service to our customers. We also participate in the EUROMOLD & FAKUMA exhibitions and meet our customers every year in Europe. By concentrating on mold “niche markets”, we play a very useful mold maker role from the Far East whenever customers want to develop their new projects. We provide services from A to Z to our customers on a very economic cost and effect basis.

  123. Avatar
    canadian levitra over 3 years later:

    Thank you for responding, but I’m interested in the existence of a functional programming language is not OO. I already know OOP.

  124. Avatar
    plastic injection mold over 3 years later:

    Intertech Machinery Inc.

    With more than 25 years of experience, Intertech provides an extensive integrated operational ability from design to production of molds 100% made in Taiwan. Additional to our own mold making factory, we also cooperate with our team vendors to form a very strong working force in Taiwan.

    Main Products:

    Injection Mold, Silicone Molding, Rubber Mold, Silicone molding, PC High-Gloss Plastic Mold, Die Casting Mold, Silicone Mold, Silicone Rubber Mold, Liquid Silicone Rubber , Cosmetic Packaging Mold, Medical Products Mold, Engineering Plastic Molds, Home Appliances Mold, etc…

Comments