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
-~----------~----~----~----~------~----~------~--~---

Reply via email to