London Clojure Dojo, September 2011
Posted on 04 October 2011
The latest clojure dojo was on a subject suggested by Robert Rees: write a program to play 20 Questions, such that the program starts with a small dataset of known celebrities, but each time it fails to guess someone, you teach it a new question. So, for example, if it had guessed Barack Obama but I was thinking of Abraham Lincoln, I could teach it to ask “Are you alive?” to distinguish between these two people later.
I decided to take the idea and extend it by writing a shared server which everyone could use. They would download the initial dataset, play a game locally, then when they were finished, upload their dataset back to the server to share with everyone else.
The code for the server is on my github. It operates via simple HTTP — there is one URL at /20-questions/latest which accepts either a GET request to pull down the data, or a PUT to push it back up.
Once again, I learned a number of lessons — last time, they were about participating in a dojo, this time, they were about organizing a dojo.
Lesson 1: User input is hard.
It’s upsetting that I hadn’t thought about this beforehand, but just like in every other language, user input in Clojure is painful, and difficult to get anything done with in the time constraints of a dojo. Although one or two of the dojo teams managed to wrestle with read-line enough to get it working, the rest of the teams ended up writing internal DSLs (or, less grandiosely, functions) to play the game without having to solve some accidental complexity. In hindsight, I might have thought more deeply about how to deal with user input.
Lesson 2: Error handling feedback is important
A number of the teams had problems uploading to the server. Uploading is always going to be harder than downloading, because you have to generate the data in the correct format, and there’s more ways for that to go wrong than for parsing the downloaded data.
The server had actually been set up to issue useful responses to problems: if it thought a request was not well formatted, or that it didn’t resemble a tree of questions, it would return a 400 (Bad request) code. If, on the other hand, it received a valid question tree, but one which was based on out of date data, it would return a 409 (Conflict) response. An example of the latter situation is where you try to upload a tree, but somebody else adds a new celebrity and beats you to it. In order to prevent people from wiping out each other’s progress, the server would reject the request and issue a 409.
Two problems prevented this from being useful feedback. The first was that I just didn’t explain this at all; I felt that it might be too much detail to go in before people had gotten their teeth into it. But I could have at least mentioned that the error codes were meaningful and that they could have asked me for help once they had reached the point of trying to upload.
The second problem was that clj-http, the library we were using, did not show very much feedback when the error responses were received. The server had, in fact, filled in a full request body, explaining the situation in plain english; but clj-http just threw an exception which said “400” or “409” – not even a “Bad request” or “Conflict”! This made the fact that there was more information available very undiscoverable.
So all in all, as I could see these problems happening, people struggling with user input or seemingly random HTTP response codes, I was getting more and more nervous that nobody would be able to upload anything to the central server — whose data, by the way, was being projected onto a big screen for everyone to see. Thankfully, after a while, one of the teams successfully uploaded, and shortly thereafter, a number of the other teams did too. By the time we were ready for show and tell, we had built up quite a question tree:
{:no {:no {:no "Elizabeth I", :question "Did you find Nemo?", :yes "Nemo's Mum"}, :question "Are you a king?", :yes {:no "Richard the 3rd", :question "Do you have blue suede shoes", :yes "Elvis"}}, :question "Are you alive?", :yes {:no {:no {:no {:no {:no "Chuck Norris", :question "Are you in the building?", :yes "Elvis"}, :question "Who's the Daddy?", :yes "The Daddy"}, :question "Are you a panda?", :yes {:no "Kung fu Panda", :question "Are you ling ling", :yes "Ling Ling"}}, :question "Are you a kung fu expert?", :yes {:no "Bruce Durling", :question "Are you (not Bruce Durling)?", :yes "Not Bruce Durling"}}, :question "are you a newsreader?", :yes {:no "Trevor McDonald", :question "Are you the Irish newsreader (the only one)?", :yes "Sharon Ni Bheolan"}}}
I think I’ve never seen such a great example of the GIGO principle.
If you want to see some of the client code, team 4 and team 5 uploaded theirs. Also, after the dojo, Uday created a zipper-based client.
The London clojure dojo happens on the last Tuesday of every month. During the dojo, we split into groups of four or five around a single computer, and each person takes a turn at the keyboard. This ensures that even if you have zero clojure experience, you will get the opportunity to write some code at the event.
Entry to the dojo is free, but advance booking is required. Listen for announcements on the London clojurians mailing list.