The 'li function could be simplified. html.arc has the facilities to define tags; coupled with 'tostring, we can capture the output, giving something like:
(attribute li class opstring)
(attribute li id opstring)
(def li items
(tostring
(each i items
(if (isa i 'table)
(tag (li class i!class id i!id) (pr i!body))
(tag li (pr i))))))
This also uses a rest parameter so that 'li can take any number of arguments and collect them up into a list:
arc> (li (obj body "hi" id "brown") (obj body "world" class "green") "foo")
"<li id=\"brown\">hi</li><li class=\"green\">world</li><li>foo</li>"
Though it doesn't quite replicate the use of 'string you had originally, e.g.,
arc> (li ''(a b c)) ; the one defined above
"<li>(quote (a b c))</li>"
arc> (li (list ''(a b c))) ; yours
"<li>quoteabc</li>"
You note that 'gentag prints all over the place. I'm sure (because of the game you built) you notice that Arc uses printing in its html-generation tools. So if you wanted to use this 'li function inside, say, a 'defop, you probably don't want to wrap it in the 'tostring -- instead, let it print so that you don't have to make the call to 'pr yourself.
I don't see how to inject request-scoped variables - it seems you can only call functions in the global namespace. In other words, how would you render
<span>hello, @(current-user)</span>
for multiple concurrent users?
A separate, and totally subjective, observation: one advantage of having prs all over the place is that output gets sent directly to the client, rather than getting buffered on the server (caveat: depending on how the stream is implemented I suppose). Writing to a string and then printing that string might be more resource-intensive; only an issue for high-volume sites though. The big win I've had with prs all over the place is that when something goes wrong (and arc isn't always very helpful when something goes wrong), you can "view source" in your browser and see where output stopped, and then hopefully make a guess as to where the error is in your code.
Feel free to use user:pass as a login. I considered using cookies, but personal and global high scores are next the list and I was heavily referencing blog.arc, besides, you only have to login once.
I wrote this because as someone who enjoys programming, lisp is/was a bit of a steep hill to climb, and hopefully this could help pave the way for others. I'll post this on HN in a bit, but just wanted some feed back. Please tell me anything I missed or your thoughts in general.
The nil is the return value of the function. Since the last thing you did was an assignment, is 'nil. It only shows up like that on the repl, which prints both stdout and return values. It is true that printing a newline first would make it show up in the next line, but it doesn't really matter if you're making it into a web service since the nil won't be printed.