I learned something about symbols and packages

:: IT, Programmierung

I am using Common Lisp for developing a web application. Several days ago a new part of this application didn’t worked as supposed and I spent a considerable large amount of time in finding the bug. It was a very simple problem with symbols where I mixed something up.

In the application the web server somewhen gets some JSON data from the browser. It is then converted to Lisp object using the CL-JSON package. This package converts JSON objects to a-lists and converts the member keys to symbols (see CL-JSON’s documentation. I then wanted to look something up in that a-list and failed.

I wrote a small test case to show the effect and explain what went wrong.

(ql:quickload '("hunchentoot" "cl-who"))
;; direct loading via ql only for demonstration purposes, normally I
;; would use a asdf:defsystem for that.

(in-package :cl-user)

(defpackage :my-app (:use :cl))

(in-package :my-app)

(defparameter *my-a-list* 
  '((foo . 100)
    (bar . 200)))   ;; in the real application this a-list is
		    ;; generated by a JSON-to-lisp conversion by
		    ;; CL-JSON; in CL-JSON the object member keys are
		    ;; converted to symbols.

(defun get-value (key)
  "Returns the value with KEY from *MY-A-LIST*."
  (cdr (assoc (intern (string-upcase key)) *my-a-list*)))

(hunchentoot:define-easy-handler (web-get-value :uri "/get-value") (id)
  (cl-who:with-html-output-to-string (*standard-output* nil :prologue t)
    (:p (cl-who:fmt "Value of ~a is: ~a" id (get-value id)))))

(defun start ()
  (hunchentoot:start (make-instance 'hunchentoot:easy-acceptor :port 4242)))

So on the REPL everything looks fine: MY-APP> (get-value "foo") 100 MY-APP> (get-value "bar") 200 MY-APP>

But when I used my web browser to give me these results as well I got something strange. For example here are some results when using curl: ~> curl http://localhost:4242/get-value?id=foo <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <p>Value of foo is: NIL</p>

I was puzzled: The value is NIL?

After some debugging I found out that the easy handler from Hunchentoot runs with *package* set to COMMON-LISP-USER (and not to MY-APP as I implicitly assumed). That means that assoc looked up COMMON-LISP-USER::FOO in the a-list where the keys are MY-APP::FOO and MY-APP::BAR. And this test fails. Therefore NIL is returned which is correct.

So I rewrote the get-value function to: (defun get-value (key) "Returns the value with KEY from *MY-A-LIST*." (cdr (assoc (intern (string-upcase key) (find-package :my-app)) *my-a-list*))) Now the symbols are interned in the same package and everything went well: ~> curl http://localhost:4242/get-value?id=foo <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <p>Value of foo ist: 100</p> ~> curl http://localhost:4242/get-value?id=bar <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <p>Value of bar ist: 200</p>

Therefore I was reminded to think about packages when interning symbols. A good guide to symbols and packages could be found in this document: The Complete Idiot’s Guide to Common Lisp Packages.