2009-01-26

Lisp is full of teh WIN

I know that the title is lame, verging on cringe-inducing, but I had to say it; I'm not sorry. There has been a few threads of discussion over on Hacker News about what's wrong with Lisp. The argument seems to hinge on the lack of a literal syntax for "hashes" in lisp the way that there is in python or, in a more appropriate comparison, clojure. Clojure is great, by the way, and it is certainly deserving of its own post sometime soon. Suffice it to say that the next time I have to write something in Java I think I'm going to write it in clojure instead. Clojure has great interoperability with Java. Amazingly, there are fewer parenthesis in the Lisp version of that little GUI program than in the Java version! But on to the topic of the day. In many languages you can do something like this:
>>> d = {"a":1, "b":2, "c":3}
>>> d
{'a': 1, 'c': 3, 'b': 2}
>>> d['b']
2
You can see that we can explicitly give a representation of that hash that python knows how to parse into the corresponding data structure. Clojure has a very similar literal syntax:
user=> (get {:a 1 :b 2 :c 3} :b)
2
So the complaint is that Common Lisp doesn't have anything like that. Using hashes in CL consists of a bunch of function calls that manage the hash:
CL-USER> (let ((hash (make-hash-table)))                                       
          (setf (gethash :a hash) 1)                                          
          (setf (gethash :b hash) 2)                                          
          (setf (gethash :c hash) 3)                                          
          (gethash :b hash))
2
T
There is never any literal representation of the hash, in fact, if we evaluate the name of the hash we'll just get something like this "#<HASH-TABLE :TEST EQL :COUNT 3 {AD11A99}>" Which basically tells the lisp reader to signal an error if the form is read. But this doesn't have to be so verbose. We can write a really simple reader macro (with a little helper function) that gives us something like what we have in python or clojure above:
(defun ins-hash (h vals)
  (if (oddp (length vals))
      nil
      (if (not (null vals))
          (progn
            (setf (gethash (first vals) h) (second vals))
            (ins-hash h (cddr vals)))
          h)))

(set-macro-character #\} (get-macro-character #\)))

(set-dispatch-macro-character #\# #\{
  #'(lambda (stream char1 char2)
      (let ((items (read-delimited-list #\} stream t)))
        (ins-hash (make-hash-table) items))))
With this reader macro I can now write this:
CL-USER> (gethash :b #{:a 1 :b 2 :c 3})
2
T
Lisp now interprets everything between '#{' and '}' as key, value pairs and using the helper function inserts them into a new hash. Cool!

1 comment:

Unknown said...

OMG, finally, I find this kind of post. It's really useful. Thank u very much. gw2 gold

twopoint718

About Me

My photo
A sciency type, but trying to branch out into other areas. After several years out in the science jungle, I'm headed back to school to see what I can make of the other side of the brain.