literate snake

8 views
Skip to first unread message

Mark Volkmann

unread,
Jan 2, 2009, 2:07:59 PM1/2/09
to clo...@googlegroups.com
I've written a new version of the snake program that uses a more
literate style and therefore, to my eyes, calls for far fewer
comments. I think this code is very readable. Check it out at
http://www.ociweb.com/mark/programming/ClojureLiterateSnake.html.
Feedback is welcomed!

The most controversial thing about this code is probably my use of def
to change the state of the snake and the apple. It's not yet clear to
me that using atoms is needed here, but I need to think about that
more.

This version has some features that weren't in the original such as:
- automatically turning the snake in the clockwise direction when a
board edge is reached
- changing the color of the snake to black when it overlaps itself
- announcing a win when the length of the snake reaches 10
- automatically restarting the game after an overlap or a win

--
R. Mark Volkmann
Object Computing, Inc.

Mark H.

unread,
Jan 2, 2009, 3:10:41 PM1/2/09
to Clojure
On Jan 2, 11:07 am, "Mark Volkmann" <r.mark.volkm...@gmail.com> wrote:
> The most controversial thing about this code is probably my use of def
> to change the state of the snake and the apple. It's not yet clear to
> me that using atoms is needed here, but I need to think about that
> more.

Not atoms, refs. Or agents for that matter -- the snake can be
naturally modeled as an agent, because it responds to events coming
from different sources:

* the movement update
* user input
* encountering objects

mfh

Tom Ayerst

unread,
Jan 2, 2009, 4:05:41 PM1/2/09
to clo...@googlegroups.com
That def inside a function doesn't look right but I'm a noob at this too.  I managed to run the snake off the board which suggests the concurrency is not quite right.

I would use 'cell' instead of 'grid'.

Cheers

Tom

2009/1/2 Mark Volkmann <r.mark....@gmail.com>

Tom Ayerst

unread,
Jan 2, 2009, 4:08:17 PM1/2/09
to clo...@googlegroups.com
Also; I think the 'get' is necessary on the get-snake-head and get-snake-body.

2009/1/2 Tom Ayerst <tom.a...@gmail.com>

Chouser

unread,
Jan 2, 2009, 4:09:12 PM1/2/09
to clo...@googlegroups.com
On Fri, Jan 2, 2009 at 4:05 PM, Tom Ayerst <tom.a...@gmail.com> wrote:
> That def inside a function doesn't look right but I'm a noob at this too. I
> managed to run the snake off the board which suggests the concurrency is not
> quite right.

Calling 'def' to like this is much worse than a lack of comments,
especially in code meant to teach.

--Chouser

Mark Volkmann

unread,
Jan 2, 2009, 4:47:25 PM1/2/09
to clo...@googlegroups.com

Thanks for the feedback! I'll gladly change it to use atoms or agents
or refs. I'm just not sure which one is most appropriate. What do you
think I should use?

Mark Volkmann

unread,
Jan 2, 2009, 4:49:24 PM1/2/09
to clo...@googlegroups.com
On Fri, Jan 2, 2009 at 3:05 PM, Tom Ayerst <tom.a...@gmail.com> wrote:
> That def inside a function doesn't look right but I'm a noob at this too. I
> managed to run the snake off the board which suggests the concurrency is not
> quite right.

Interesting. I can't duplicate that.

> I would use 'cell' instead of 'grid'.

I'll make that change in the next version I post later today.

Thanks for the feedback!

Mark Volkmann

unread,
Jan 2, 2009, 4:52:47 PM1/2/09
to clo...@googlegroups.com
On Fri, Jan 2, 2009 at 3:08 PM, Tom Ayerst <tom.a...@gmail.com> wrote:
> Also; I think the 'get' is necessary on the get-snake-head and
> get-snake-body.

I assume you meant that "get-" is NOT necessary. I considered that,
but I wanted the names of my functions that were not predicates to be
verb-like. For example, that's why I chose the name "make-snake"
instead of "new-snake". I don't know that anyone has documented a
recommended naming convention for Clojure yet, but if there was a
convention I would gladly follow it.

Chouser

unread,
Jan 2, 2009, 5:04:32 PM1/2/09
to clo...@googlegroups.com

I don't feel I have much authority in the realm of designing
concurrent programs, but here are a couple thoughts:

It seems to me that 'apple' and 'snake' together describe the state of
the game, and therefore need their changes coordinated. This suggests
they should both be refs.

I think it might also be very natural to use a single agent with
Thread/sleep and send-off to itself instead of the Timer. This would
detangle the actionPerformed method from the panel, and allow you to
give the action function a meaningful name instead.

--Chouser

Mark Volkmann

unread,
Jan 2, 2009, 5:20:19 PM1/2/09
to clo...@googlegroups.com
On Fri, Jan 2, 2009 at 4:04 PM, Chouser <cho...@gmail.com> wrote:
>
> n Fri, Jan 2, 2009 at 4:47 PM, Mark Volkmann <r.mark....@gmail.com> wrote:
>>
>> On Fri, Jan 2, 2009 at 3:09 PM, Chouser <cho...@gmail.com> wrote:
>>>
>>> On Fri, Jan 2, 2009 at 4:05 PM, Tom Ayerst <tom.a...@gmail.com> wrote:
>>>> That def inside a function doesn't look right but I'm a noob at this too. I
>>>> managed to run the snake off the board which suggests the concurrency is not
>>>> quite right.
>>>
>>> Calling 'def' to like this is much worse than a lack of comments,
>>> especially in code meant to teach.
>>
>> Thanks for the feedback! I'll gladly change it to use atoms or agents
>> or refs. I'm just not sure which one is most appropriate. What do you
>> think I should use?
>
> I don't feel I have much authority in the realm of designing
> concurrent programs, but here are a couple thoughts:
>
> It seems to me that 'apple' and 'snake' together describe the state of
> the game, and therefore need their changes coordinated.

I'm thinking their changes don't need to be coordinated. Here's why.
The only state for an apple is it's color and cell. The color never
changes. The cell only changes when the snake eats it. That is
determined in the actionPerformed method which is also responsible for
triggering movement of the snake, thus changing its state.
actionPerformed is called on the Swing event dispatch thread. That
means there can never multiple threads running actionPerformed
concurrently. I think this is why my code works using def, despite it
being bad form.

> This suggests they should both be refs.
>
> I think it might also be very natural to use a single agent with
> Thread/sleep and send-off to itself instead of the Timer. This would
> detangle the actionPerformed method from the panel, and allow you to
> give the action function a meaningful name instead.

It would also invalidate what I said above about knowing that the
actionPerformed code was always being executed from a single thread.
Do you think it's a good idea to do that anyway?

Christian Vest Hansen

unread,
Jan 2, 2009, 5:23:42 PM1/2/09
to clo...@googlegroups.com
What is it that makes this code "literate"?
--
Venlig hilsen / Kind regards,
Christian Vest Hansen.

Mark Volkmann

unread,
Jan 2, 2009, 5:34:15 PM1/2/09
to clo...@googlegroups.com
On Fri, Jan 2, 2009 at 4:23 PM, Christian Vest Hansen
<karma...@gmail.com> wrote:
>
> What is it that makes this code "literate"?

Perhaps my understanding of the term is a bit off. What makes this
code different from most Clojure code I see is that the functions tend
to be very short and focused. I think this makes reading the code much
easier. I don't feel like I have to think as hard to figure out what
each piece is doing. This makes me more comfortable with including
almost no comments. For example, my previous version contained this:

(paintComponent [graphics]
(proxy-super paintComponent graphics)
(paint graphics @apple (colors :apple))
(doseq [point (:body @snake)]
(paint graphics point (colors :snake))))

My new version contains this:

(paintComponent [graphics]
(proxy-super paintComponent graphics)
(paint-apple graphics)
(paint-snake graphics))

This is a style that is strongly encouraged in Smalltalk, and perhaps
in many other programming communities. It's really just lots of
application of the "extract method" refactoring pattern.

Randall R Schulz

unread,
Jan 2, 2009, 5:38:21 PM1/2/09
to clo...@googlegroups.com
On Friday 02 January 2009 14:23, Christian Vest Hansen wrote:
> What is it that makes this code "literate"?

I don't know whether or not you're familiar with the concept of Literate
Programming. If you are, then you can judge for yourself whether that
code qualifies as literate. If not, check out some of these references:

- <http://www-cs-faculty.stanford.edu/~knuth/lp.html>
- <http://en.wikipedia.org/wiki/Literate_programming>
- <http://www.literateprogramming.com/>
- <http://www.literateprogramming.com/knuthweb.pdf>
- <http://vasc.ri.cmu.edu/old_help/Programming/Literate/literate.html>

Many more are out there.


Randall Schulz

Chouser

unread,
Jan 2, 2009, 5:48:49 PM1/2/09
to clo...@googlegroups.com
On Fri, Jan 2, 2009 at 5:20 PM, Mark Volkmann <r.mark....@gmail.com> wrote:
>
> On Fri, Jan 2, 2009 at 4:04 PM, Chouser <cho...@gmail.com> wrote:
>>
>> I don't feel I have much authority in the realm of designing
>> concurrent programs, but here are a couple thoughts:
>>
>> It seems to me that 'apple' and 'snake' together describe the state of
>> the game, and therefore need their changes coordinated.
>
> I'm thinking their changes don't need to be coordinated. Here's why.
> The only state for an apple is it's color and cell. The color never
> changes. The cell only changes when the snake eats it. That is
> determined in the actionPerformed method which is also responsible for
> triggering movement of the snake, thus changing its state.

But if any other thread were to observe apple and snake immediately
after the apple has been moved (because of a collision), it might be
surprised to find that a moment later the snake has grown even though
the apple was nowhere near it (having already been moved).

Worse yet, suppose some other thread moves the apple, perhaps because
the player is taking too long in driving the snake to it. If this
were to happen after the collision detection but before the apple is
moved, you'd again get unexpected results.

You can know of course that neither of these will happen because there
are no other threads doing any such thing. But (1) that may change in
a future version of the program and (2) even as it is this requires
you to reason about the *entire* program, not just the part you're
dealing with at the moment.

Thus I'd argue that for a correct and robust program, you should use
refs for apple and snake, and think carefully about the appropriate
scope for the dosync(s) that you use to modify them.

> actionPerformed is called on the Swing event dispatch thread.

This means that while the "application logic" part of the program is
running, the UI will not respond to user interaction. Again, this
hardly matters for a toy program, but is something you'd want to avoid
when demonstrating the "right way" to do things.

>> I think it might also be very natural to use a single agent with
>> Thread/sleep and send-off to itself instead of the Timer. This would
>> detangle the actionPerformed method from the panel, and allow you to
>> give the action function a meaningful name instead.
>
> It would also invalidate what I said above about knowing that the
> actionPerformed code was always being executed from a single thread.
> Do you think it's a good idea to do that anyway?

actionPerformed would no longer be run in the Swing thread, and might
indeed be run in different threads at different times, but as long as
it was always run on the same agent, you're guaranteed that no two
actionPerformed would be run at the same time. Actions are serialized
for a given agent.

So yes, I'd still say it's a good idea. Though you would want to use
invokeLater in your new-game function.

--Chouser

Mark Volkmann

unread,
Jan 2, 2009, 5:51:36 PM1/2/09
to clo...@googlegroups.com

Thanks Randall! Clearly what I'm doing doesn't fit the definition of
literate programming. Maybe I can claim that it's "literate style"
based on this part of the definition:

"The main idea is to treat a program as a piece of literature,
addressed to human beings rather than to a computer."

What I'm trying to do is break the code up into a number of helper
functions so the the functions that use them are easier to read. For
example, here's a snippet of my code (including a questionable use of
def that will be changed soon):

(if (snake :alive)
(if (adjacent-or-same-cell? (snake-head) (apple :cell))
(do
(def apple (make-apple))
(move-snake true)
(if (= (snake-length) *length-to-win*)
(new-game "You win!")))
(move-snake false))
(new-game "You killed the snake!"))

I should probably change the arguments to move-snake to be more
meaningful. You get the idea though. What I'm trying to avoid is
deeply nested function definitions with lots of long argument lists
and anonymous functions.

Mark Volkmann

unread,
Jan 2, 2009, 5:54:17 PM1/2/09
to clo...@googlegroups.com

Thanks Chris! Your explanation makes a lot of sense. I'll change the
code as you suggested and put up a new version soon.

lpetit

unread,
Jan 2, 2009, 6:29:12 PM1/2/09
to Clojure
Yes, what you did should certainly be called "Intentional
programming" (or "Intention revealing programming") instead of
"literate programming".

This style of programming is for example encouraged by the book "Clean
Code" of Robert C. Martin.

I like this style of programming, I too think it is clear, and makes a
good use of the compiler that prevents any "comment" (considering the
function names are the old "comments) from being not up to date ! :-)

The difficulty, as always, is in finding the "right balance" !

HTH,

--
Laurent

On Jan 2, 11:51 pm, "Mark Volkmann" <r.mark.volkm...@gmail.com> wrote:

lpetit

unread,
Jan 2, 2009, 6:40:26 PM1/2/09
to Clojure
Hello,

here are some feedbacks :

I suggest you should create a namespace for the code of the game
('cause you want to show clojure good coding practices, as well as
good coding conventions, won't you ;-)

Could it make sense to use even fewer def's than currently ?
I guess it could be made not mandatory to have apple, snake, as global
vars ?
And the JFrame too ?

It could be great to have just defn's (and defstruct's and
defmacro's), and a single defn entry point for starting the game ?

HTH,

--
Laurent

Mark Volkmann

unread,
Jan 2, 2009, 7:15:25 PM1/2/09
to clo...@googlegroups.com
On Fri, Jan 2, 2009 at 5:29 PM, lpetit <lauren...@gmail.com> wrote:
>
> Yes, what you did should certainly be called "Intentional
> programming" (or "Intention revealing programming") instead of
> "literate programming".

I was considering referring to "intentional" rather than "literate"
until I saw this in the Wikipedia description of "intentional
programming".

'Key to the benefits of IP is that source code is not stored in text
files, but in a binary file that bears a resemblance to XML."

> This style of programming is for example encouraged by the book "Clean
> Code" of Robert C. Martin.
>
> I like this style of programming, I too think it is clear, and makes a
> good use of the compiler that prevents any "comment" (considering the
> function names are the old "comments) from being not up to date ! :-)
>
> The difficulty, as always, is in finding the "right balance" !

Thanks for the feedback!

Mark Volkmann

unread,
Jan 2, 2009, 7:47:55 PM1/2/09
to clo...@googlegroups.com
On Fri, Jan 2, 2009 at 5:40 PM, lpetit <lauren...@gmail.com> wrote:
>
> Hello,
>
> here are some feedbacks :
>
> I suggest you should create a namespace for the code of the game
> ('cause you want to show clojure good coding practices, as well as
> good coding conventions, won't you ;-)

Right! Thanks for the suggestion. I've added that.

> Could it make sense to use even fewer def's than currently ?
> I guess it could be made not mandatory to have apple, snake, as global
> vars ?
> And the JFrame too ?

I'm not sure how to get rid of any of those defs. I need access to the
JFrame in the new-game function.

> It could be great to have just defn's (and defstruct's and
> defmacro's), and a single defn entry point for starting the game ?

Are you suggesting that I create a single struct that holds what are
currently global variables in my code? Maybe I should create a global
map named game-config to hold those. Suggestions welcomed!

Mark Volkmann

unread,
Jan 2, 2009, 8:39:54 PM1/2/09
to clo...@googlegroups.com
For anyone still following this, the latest code that incorporates
many of the suggestions I've received here is at
http://www.ociweb.com/mark/programming/ClojureSnake.html, replacing my
original version. It now uses refs. I think I have the dosyncs
optimally placed. Feedback still welcomed! Can I / should I further
reduce the number of defs I have?

John Newman

unread,
Jan 2, 2009, 10:31:49 PM1/2/09
to clo...@googlegroups.com
I don't know much about functional programming, but I believe you're supposed to think about functions as black boxes.  Put something in, get something out.

Take this function, for example:

(defn snake-head [] (first (@snake :body)))

(We're cheating on the put something in part!)

Perhaps it'd be better if we didn't instantiate the snake until later in the "game loop." What if we wanted to make a game with two snakes?  Then snake-head is broken.

Rather, we just do something like:

(defn snake-head [any-snake] (first (@any-snake :body)))

Now many snakes from many threads can use this function. (I think.. I'm noobs, so... :)

In fact, in most game tutorials, the "game loop" will be in a separate thread all together, so it might be more common to leave any def'ing of apples and snakes toward the bottom, where you have the gui code.

As a side note, if I remember correctly, other Java game tutorials usually frame up the gui fairly early in the code.  They probably do that for aesthetic reasons -- so the Java app seems to load faster.  Don't quote me on that though.

2cents
--
John

Mark H.

unread,
Jan 3, 2009, 1:14:48 AM1/3/09
to Clojure
On Jan 2, 5:39 pm, "Mark Volkmann" <r.mark.volkm...@gmail.com> wrote:
> For anyone still following this, the latest code that incorporates
> many of the suggestions I've received here is athttp://www.ociweb.com/mark/programming/ClojureSnake.html, replacing my
> original version. It now uses refs. I think I have the dosyncs
> optimally placed.

I noticed that the new-game function which is called in a transaction
does IO (it displays "You killed the snake!"). Transactions may be
retried (see http://clojure.org/refs) so that message might get
displayed more than once. There's an "io!" macro (doesn't seem to be
documented on the web page, but it's in core.clj in the Clojure source
with a nice docstring) which serves only to help programmers avoid
putting IO in their transactions -- it doesn't magically make IO work
right in a transaction. (Refs and transactions don't solve the
problem that IO is inherently stateful and destructive -- Clojure
relies on the IO system itself to handle that, which it should nicely
in your case.)

mfh

Brian Will

unread,
Jan 3, 2009, 4:03:42 AM1/3/09
to Clojure
The problem with the snake running off the edge simply is a flaw in
your logic here:

(defn verify-direction
"Gets the current snake direction or
a new one if a board edge was reached."
[]
(let [direction (@snake :direction)
head (snake-head)
x (head :x)
y (head :y)]
(cond
(and (= direction :right) (= x (- *board-width* 1))) :down
(and (= direction :down) (= y (- *board-height* 1))) :left
(and (= direction :left) (= x 0)) :up
(and (= direction :up) (= y 0)) :right
true direction)))

Each case of the cond here is flawed. For example, when the snake is
heading right and already on the bottom, he's going to turn down,
which is off the board.

I suggest splitting each direction case into two cases such that the
snake goes in the direction where there's more room to travel:

(defn verify-direction
"Gets the current snake direction or
a new one if a board edge was reached."
[]
(let [direction (@snake :direction)
head (snake-head)
x (head :x)
y (head :y)
left (= x 0)
right (= x (- *board-width* 1))
top (= y 0)
bottom (= y (- *board-height* 1))
left-half (< x (/ *board-width* 2))
right-half (>= x (/ *board-width* 2))
top-half (< y (/ *board-height* 2))
bottom-half (>= y (/ *board-height* 2))]
(cond
(and (= direction :right) right top-half) :down
(and (= direction :right) right bottom-half) :up
(and (= direction :down) bottom left-half) :right
(and (= direction :down) bottom right-half) :left
(and (= direction :left) left top-half) :down
(and (= direction :left) left bottom-half) :up
(and (= direction :up) top left-half) :right
(and (= direction :up) top right-half) :left
true direction)))

--Brian Will

On Jan 2, 11:07 am, "Mark Volkmann" <r.mark.volkm...@gmail.com> wrote:
> I've written a new version of the snake program that uses a more
> literate style and therefore, to my eyes, calls for far fewer
> comments. I think this code is very readable. Check it out athttp://www.ociweb.com/mark/programming/ClojureLiterateSnake.html.
> Feedback is welcomed!
>
> The most controversial thing about this code is probably my use of def
> to change the state of the snake and the apple. It's not yet clear to
> me that using atoms is needed here, but I need to think about that
> more.
>
> This version has some features that weren't in the original such as:
> - automatically turning the snake in the clockwise direction when aboardedge is reached

Mark Volkmann

unread,
Jan 3, 2009, 12:28:22 PM1/3/09
to clo...@googlegroups.com

Thanks Brian! I ended up doing something similar that required a
little less code. Here's what I did.

(let [direction (@snake :direction)
head (snake-head)
x (head :x)
y (head :y)

at-left (= x 0)
at-right (= x (- *board-width* 1))
at-top (= y 0)
at-bottom (= y (- *board-height* 1))]
(cond
(and (= direction :up) at-top) (if at-right :left :right)
(and (= direction :right) at-right) (if at-bottom :up :down)
(and (= direction :down) at-bottom) (if at-left :right :left)
(and (= direction :left) at-left) (if at-top :down :up)
true direction)))

Mark Volkmann

unread,
Jan 3, 2009, 12:56:36 PM1/3/09
to clo...@googlegroups.com
On Fri, Jan 2, 2009 at 9:31 PM, John Newman <joh...@gmail.com> wrote:
> I don't know much about functional programming, but I believe you're
> supposed to think about functions as black boxes. Put something in, get
> something out.
>
> Take this function, for example:
>
> (defn snake-head [] (first (@snake :body)))
>
> (We're cheating on the put something in part!)
>
> Perhaps it'd be better if we didn't instantiate the snake until later in the
> "game loop." What if we wanted to make a game with two snakes? Then
> snake-head is broken.
>
> Rather, we just do something like:
>
> (defn snake-head [any-snake] (first (@any-snake :body)))
>
> Now many snakes from many threads can use this function. (I think.. I'm
> noobs, so... :)

I think you're probably right. I wonder though if it's better to make
the callers do the dereference instead of doing it in the snake-head
function. What do others think about this?

> In fact, in most game tutorials, the "game loop" will be in a separate
> thread all together, so it might be more common to leave any def'ing of
> apples and snakes toward the bottom, where you have the gui code.

Yeah, I need to work on making that change.

> As a side note, if I remember correctly, other Java game tutorials usually
> frame up the gui fairly early in the code. They probably do that for
> aesthetic reasons -- so the Java app seems to load faster. Don't quote me
> on that though.

Thanks for the feedback!

Mark Volkmann

unread,
Jan 3, 2009, 1:14:30 PM1/3/09
to clo...@googlegroups.com

What do you think of this way of resolving the issue? Note the use of
the local variable "message". That can get set to either "You win!" or
"You killed the snake!" inside dosync. Then outside dosync I check
that and call new-game only if it's set.

(defn process-move []
(with-local-vars [message nil]
(dosync
(if (@snake :alive)
(if (adjacent-or-same-cell? (snake-head) (@apple :cell))
; Use the next line instead to require collision with the apple.
;(if (= (snake-head) (@apple :cell))
(do
(ref-set apple (make-apple))


(move-snake true)
(if (= (snake-length) *length-to-win*)

(var-set message "You win!")))
(move-snake false))
(var-set message "You killed the snake!"))))
(if (var-get message) (new-game (var-get message)))))

Brian Will

unread,
Jan 3, 2009, 4:53:29 PM1/3/09
to Clojure
My way was a little verbose, but you could do:

(defn verify-direction
"Gets the current snake direction or
a new one if a board edge was reached."
[]
(let [direction (@snake :direction)
head (snake-head)
x (head :x)
y (head :y)
at-left (= x 0)
at-right (= x (- *board-width* 1))
at-top (= y 0)
at-bottom (= y (- *board-height* 1))
left-half (< x (/ *board-width* 2))
top-half (< y (/ *board-height* 2))]
(cond
(and (= direction :right) at-right) (if top-half :down :up)
(and (= direction :down) at-bottom) (if left-half :right :left)
(and (= direction :left) at-left) (if top-half :down :up)
(and (= direction :up) at-top) (if left-half :right :left)
true direction)))

On Jan 3, 9:28 am, "Mark Volkmann" <r.mark.volkm...@gmail.com> wrote:

Mark H.

unread,
Jan 3, 2009, 10:12:05 PM1/3/09
to Clojure
On Jan 3, 10:14 am, "Mark Volkmann" <r.mark.volkm...@gmail.com> wrote:
> What do you think of this way of resolving the issue? Note the use of the local variable "message". That can get set to either "You win!" or "You killed the snake!" inside dosync. Then outside dosync I check that and call new-game only if it's set.

That is correct with respect to transactions. I guess I would
structure the code differently: I would treat each move as a function
M: state -> (state, {win, loss, continue}) and check the second return
value for win or loss each time. M can be a pure function, except for
the IO and for telling, say, a display agent about the new state of
the world. But your way works too. I'm not sure which is better.

mfh

Timothy Pratley

unread,
Jan 4, 2009, 5:48:33 AM1/4/09
to Clojure
From a cursory examination of "literate programming" central tenants
appear to be:
(1) order by human logic
(2) use descriptive macros
(3) program is a web

(1) Is not possible in Clojure because it resolves symbols as it reads
them. However that is easy to work around with a trivial patch (see
http://groups.google.com/group/clojure/web/auto-def.patch). auto-def
as the name suggests just defs a symbol that it can't find. So this
allows you to declare functions out of order - top down - and hope
they get redefed before any evaluation occurs. If evaluation does
occur the error still has a stack showing where and what the var is
(which will be nil - similar to a null pointer exception in java).
Here is an example that will only work with auto-def:
http://groups.google.com/group/clojure/web/lit-wc.clj

(2) lisp is great for this with hyphenation as standard. (3) yikes!
need to contemplate this.

What are the arguments against *auto-def*?

Tom Ayerst

unread,
Jan 4, 2009, 4:51:32 PM1/4/09
to clo...@googlegroups.com
I tidied up a couple of things:
Changed function name 'verify-direction' to 'new-direction' (it is more than a simple verification)
Passed 'direction' and 'snake-head' to 'new-direction' to avoid accessing global state from inside the function
Used destructuring to simplify let statement in new-head (just to see what it looked like)
Extracted dy,dx calculation in 'move-snake' into a 'delta' function

I notice everything seems to happen in the assignment vector in the 'let' in move-snake?  Is that good style?  I'm not sure.

What would be interesting would be to have a second randomly moving snake to avoid, that would force out some issues with global state etc.

The new code at the end:
  ; Only run the application automatically if run as a script,
  ; not if loaded in a REPL with load-file.
  (println "*command-line-args* =" *command-line-args*)
  (if *command-line-args* (main))

Doesn't do what it says for me, it printlns and stops from clj, any idea what I am doing wrong?

Cheers

Tom



2009/1/3 Brian Will <brian.th...@gmail.com>
snake.clj

Mark Volkmann

unread,
Jan 4, 2009, 5:21:48 PM1/4/09
to clo...@googlegroups.com
On Sun, Jan 4, 2009 at 3:51 PM, Tom Ayerst <tom.a...@gmail.com> wrote:
> I tidied up a couple of things:
> Changed function name 'verify-direction' to 'new-direction' (it is more than
> a simple verification)

Sounds good. I made that change to mine.

> Passed 'direction' and 'snake-head' to 'new-direction' to avoid accessing
> global state from inside the function

I also made a change to avoid that, but I just passed the dereferenced
snake rather than the direction and head.

> Used destructuring to simplify let statement in new-head (just to see what
> it looked like)

I guess I could do that even though I'm passing the whole snake
struct, but going two levels deep in destructuring is probably
confusing for readers.

> Extracted dy,dx calculation in 'move-snake' into a 'delta' function

That's nice! I'll do that too.

> I notice everything seems to happen in the assignment vector in the 'let' in
> move-snake? Is that good style? I'm not sure.

I'm not either, but it seems pretty readable to me.

> What would be interesting would be to have a second randomly moving snake to
> avoid, that would force out some issues with global state etc.

Yeah, that would be interesting.

> The new code at the end:
> ; Only run the application automatically if run as a script,
> ; not if loaded in a REPL with load-file.
> (println "*command-line-args* =" *command-line-args*)
> (if *command-line-args* (main))
> Doesn't do what it says for me, it printlns and stops from clj, any idea
> what I am doing wrong?

I think it's related to details of your clj script. The relevant like
in mine is this:

java -cp $CP clojure.lang.Script $1 -- $*

I can send you my whole clj script if you'd like.

The latest version of my code that incorporates your changes is at
http://www.ociweb.com/mark/programming/ClojureSnake.html.

Thanks Tom!

Christian Vest Hansen

unread,
Jan 5, 2009, 12:11:25 AM1/5/09
to clo...@googlegroups.com
On Sun, Jan 4, 2009 at 11:48 AM, Timothy Pratley
<timothy...@gmail.com> wrote:
>
> From a cursory examination of "literate programming" central tenants
> appear to be:
> (1) order by human logic
> (2) use descriptive macros
> (3) program is a web
>
> (1) Is not possible in Clojure because it resolves symbols as it reads
> them. However that is easy to work around with a trivial patch (see
> http://groups.google.com/group/clojure/web/auto-def.patch). auto-def
> as the name suggests just defs a symbol that it can't find. So this
> allows you to declare functions out of order - top down - and hope
> they get redefed before any evaluation occurs. If evaluation does
> occur the error still has a stack showing where and what the var is
> (which will be nil - similar to a null pointer exception in java).
> Here is an example that will only work with auto-def:
> http://groups.google.com/group/clojure/web/lit-wc.clj
>
> (2) lisp is great for this with hyphenation as standard. (3) yikes!
> need to contemplate this.
>
> What are the arguments against *auto-def*?

It's going off topic, but...

It makes you believe that symbols, like class members in Java, can be
defined in any order and that the existence of the correct symbols
will be properly checked at compile time. Yet this assumption would be
wrong, no?

Reply all
Reply to author
Forward
0 new messages