Prototyping hashtags with Common Lisp

3 minute read Published:

Common Lisp is really fun to work with. In fact emacs & slime is the best development environment that I’ve ever worked with and everytime I work on something I want to try and create it with CL.

Here I’ll try to share my experiences with how I prototyped my first working example of a simple web application.

Recently I participated in a Youth Exchange where we created some image based propaganda around gender issues. There was then the proposition to create a web application where we could share our pictures and vote between us for what should become public. Plain and simple, right?

The idea was to have people propose designs and communicate for features. But since I was the only technical person on-board I was responsible for the whole development. After some simple specifications agreement I started.

It took me just 4 hours to have a prototype that worked pretty much as we wanted. It included authenticating, image uploading to local storage, searching through hashtags and a voting system to handle whethere images would be local or not. Not a lot but still. And then all the functionality was custom made. Plain old hunchentoot and cl-who and cl-ppcre. Less than 500 LOC. For me that sounds pretty productive.

We then had the issue of deploying the prototype for gathering reviews. Thankfully there is this pretty good buildpack for heroku so it was really trivial to deploy there. Just two issues that I encountered: First because the compile.lisp in the buildpack uses CCL specific functions I had to fork it and and change it to one that could also run with SBCL. The second issue was that it can’t recognise the entry-point for the app if it isn’t loaded in the CL-USER package. This was easily remedied by just putting the init function there, although this isn’t the best style.

Probably the most involved thing in the process was the hashtag feature. Prototyping was a matter of a very simple model:

   (defvar *hashtags* '())

   (defvar *hashtag-scanner*
   (cl-ppcre:create-scanner "(?i)#([A-Za-z0-9]*)"))

   (defun add-hashtag (new-picture)
     (let* ((description (description new-picture))
	    (hashtags (cl-ppcre:all-matches-as-strings
		       *hashtag-scanner* description)))
       (loop for hashtag in hashtags
	  if (assoc hashtag *hashtags* :test #'equal)
	  do  (push new-picture (cdr (assoc hashtag *hashtags* :test #'equal)))
	  else
	  do (push (cons hashtag (list new-picture)) *hashtags*))))

(defun hashtag-pictures (hashtag)
  "Return a list of the pictures that match the given hashtag."
  (cdr (assoc (format nil "#~a" hashtag) *hashtags* :test #'equal)))

Yay association lists! Quite hacky stuff but then quite easy to test functionality.

Then just have to transform text in our views:

(defun links-for-hashtags (description uri)
  "Given a description string return the string with the appropriate
html links. Take notice that it removes the prescending #."
  (cl-ppcre:regex-replace-all *hashtag-scanner* description
			      (format nil "<a href=\"~a?tag=\\1\"> \\& </a>" uri)))

See the expressive power? Just few lines of code to test something and trivialy extend a system. Add that with the developing experience you get and for me Common Lisp is a winner. It may not have the hugest ecosystem of libraries and stuff but it has mature libraries and also it works nicely with guix so I don’t even use quicklisp anymore. Don’t take that as a bad opinion though quicklisp is great, not like npm(!).

The next step is probably creating a more modern interface though for this I really like Reagent from the Clojure camp.