Speaking for myself, as the author of the original Snake example, I had no intention of converting developers to Clojure, or of producing instructive or readable code, with that snippet.
While I agree with some of your critique, I do think it is misplaced. The aim of this particular exercise was to produce an _abnormally_ terse program, after all. If you believe there is some utility in producing a more readable version, as a tutorial, do not hesitate to make one and publish it! I'll be happy to direct readers to it. :-) On 12/29/08, Mark Volkmann <r.mark.volkm...@gmail.com> wrote: > > On Sun, Dec 28, 2008 at 9:15 PM, Rich Hickey <richhic...@gmail.com> wrote: >> >> On Dec 28, 8:13 pm, "Mark Volkmann" <r.mark.volkm...@gmail.com> wrote: >>> This is related to the thread titled "Proxying in Clojure". I've spent >>> a lot of time studying the code in this "snake" example and have some >>> observations I'd like to share. >>> >>> First off, obviously I care about the future of Clojure or I wouldn't >>> have spent the last six weeks or so trying to learn it. >>> >>> I imagine that almost everyone on this mailing list would like to have >>> the opportunity to spend more time on the job coding in Clojure than >>> they get to currently. One way to make that more likely is to make it >>> easier for others to learn Clojure. You may not be allowed to use it >>> for production code if you're the only one in your company that can >>> understand it. >>> >>> Many examples of Clojure code work counter to this goal. This snake >>> code is just one of them. I don't mean to pick on the authors. This >>> code is very similar to other Clojure code I encounter daily. >>> >>> Here are some things I think we could all do in our Clojure code to >>> make it easier for others to understand. >>> >>> 1) There are no prizes for using one letter variable names. Pick more >>> meaningful names. >>> >>> For example, (defn collision? [{[b] :body} a] ...). >>> What is a? It's a vector containing the x/y coordinates of the apple >>> the snake is trying to eat. Why not name it "apple" or >>> "apple-location"? >>> What is b? It's a vector that represents the x/y coordinates of the >>> head of the snake. >>> Why not name it "head" or "snake-head"? >>> >>> 2) There are no prizes for writing code with zero comments. One of the >>> strengths of Clojure is that you can accomplish a large amount in a >>> small amount of code. That doesn't mean that readers of your code will >>> know what is going on though. >>> >>> For example, what does this do? >>> (every? #(<= (- (a %) 10) (b %) (+ 10 (a %))) [0 1]) >>> Well, 0 and 1 are indexes of the x and y coordinates represented by >>> two element vectors that represent the location of the apple and the >>> head of the snake. This tests whether every coordinate (the x and y) >>> of the apple are "close" to the corresponding coordinate of the snake >>> head. This certainly needs to be explained in a comment. >>> >>> 3) There are no prizes for cramming loads of functionality into a >>> single line. Spread it out to make it easier to read. >>> >>> For example, which of these is easier to understand? >>> >>> ; original >>> (assoc snake :body (cons (vec (map #(+ (dir %) ((first body) %)) [0 1])) >>> (if grow body (butlast body))))) >>> >>> What is being cons'ed to what here? >>> >>> ; modified >>> (assoc snake :body >>> (cons >>> (vec (map #(+ (dir %) ((first body) %)) [0 1])) >>> (if grow body (butlast body)) >>> ) >>> ) >>> >>> I know my placement of the closing parens on separate lines is >>> non-standard in the Lisp world, but I find it helps me see better >>> where constructs end. Without doing that it feels like Python where >>> indentation is significant. I could concede the paren placement >>> though. >>> >>> Below is the original code. Compare it to my version >>> athttp://pastie.org/348031where variables are renamed, comments are >>> added, and indentation is changed in a way that I feel makes it more >>> readable. >>> >>> (import '(java.awt Color) '(javax.swing JPanel JFrame Timer) >>> '(java.awt.event KeyEvent ActionListener KeyListener)) >>> >>> (defn gen-apple [_] [(rand-int 750) (rand-int 550)]) >>> (defn move [{:keys [body dir] :as snake} & grow] >>> (assoc snake :body (cons (vec (map #(+ (dir %) ((first body) %)) [0 1])) >>> (if grow body (butlast body))))) >>> (defn turn [snake newdir] (if newdir (assoc snake :dir newdir) snake)) >>> (defn collision? [{[b] :body} a] >>> (every? #(<= (- (a %) 10) (b %) (+ 10 (a %))) [0 1])) >>> (defn paint [g p c] (.setColor g c) (.fillRect g (p 0) (p 1) 10 10)) >>> >>> (def dirs {KeyEvent/VK_LEFT [-10 0] KeyEvent/VK_RIGHT [10 0] >>> KeyEvent/VK_UP [0 -10] KeyEvent/VK_DOWN [0 10]}) >>> (def apple (atom (gen-apple nil))) >>> (def snake (atom {:body (list [10 10]) :dir [10 0]})) >>> (def colors {:apple (Color. 210 50 90) :snake (Color. 15 160 70)}) >>> (def panel (proxy [JPanel ActionListener KeyListener] [] >>> >>> (paintComponent [g] (proxy-super paintComponent g) >>> (paint g @apple (colors :apple)) >>> (doseq [p (:body @snake)] >>> (paint g p (colors :snake)))) >>> (actionPerformed [e] (if (collision? @snake @apple) >>> (do (swap! apple gen-apple) >>> (swap! snake move :grow)) >>> (swap! snake move)) >>> (.repaint this)) >>> (keyPressed [e] (swap! snake turn (dirs (.getKeyCode e)))) >>> (keyReleased [e]) >>> (keyTyped [e]))) >>> >>> (doto panel (.setFocusable true)(.addKeyListener panel)) >>> (doto (JFrame. "Snake")(.add panel)(.setSize 800 600)(.setVisible true)) >>> (.start (Timer. 75 panel)) >>> >>> Another example of a fairly short piece of code that is begging for >>> comments is the example athttp://clojure.org/concurrent_programming. >>> Imagine someone who has only been learning Clojure for a couple of >>> days and is curious about how Clojure handles concurrency trying to >>> follow what is happening in that code. >>> >>> So my main point is that if we all make an effort to make our code >>> easier to read, it will be easier to convince other developers to >>> consider learning Clojure which will be good for all of us. >> >> I'll not argue for making code harder to read, but I have to object to >> most of your example. >> >> Making something 4x longer does not make it easier to read. >> >> Redundant comments are useless. > > I agree ... if they are really redundant. For example, I don't feel > that explaining the use of [0, 1] to represent indexes for x and y > values is redundant. > >> Trailing parens are known bad - don't use them. > > I'm not disagreeing, but I'd like to hear the explanation for why they > are bad. The ones that end defn's seem the same as Java's trailing > braces to me. > >> Though when first learning you might wish every piece of code had a >> built-in language tutorial, I'd be dismayed if, e.g., every use of >> proxy contained this redundant description of how it works: >> >> ; Create a proxy object that extends JPanel and >> ; implements both ActionListener and KeyListener. >> (proxy [JPanel ActionListener KeyListener] >> [] ; arguments to the superclass constructor (none here) >> >> ; What follows are implementations of the interface methods. > > Right. I agree with you there and I did put those in my code because > that was my first exposure to Clojure proxies. > >> etc. Most of your comments don't say anything that the code doesn't. >> Those that do may be worthwhile, those that don't should not be in non- >> tutorial code. >> >> The original code was not intended as a tutorial, nor is most code, >> nor should it be. >> >> 'X in Y lines of code' examples are inherently trying to be concise, >> and probably not the best things to learn from initially. > > Even a Clojure expert would likely pause for a minute to figure out > what is going on in this code in the absence of any comments. > > I still think there's a benefit in experienced Clojure developers > taking the extra effort to make their code more accessible to Clojure > newbies, especially if we want the community to grow. > > -- > R. Mark Volkmann > Object Computing, Inc. > > > > -- Abhishek Reddy http://abhishek.geek.nz --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~----------~----~----~----~------~----~------~--~---