From ... Path: archiver1.google.com!news1.google.com!sn-xit-02!supernews.com!news.tele.dk!small.news.tele.dk!129.240.148.23!uio.no!Norway.EU.net!news01.chello.no!not-for-mail Newsgroups: comp.lang.lisp Subject: Re: looking for a language with any of the following 4 charachteristics (all 4 would be nice). References: <42e37222.0202111748.77f8a64a@posting.google.com> <3C697B24.9D1A372@alltel.net> <3222540975992591@naggum.net> <863d04es3m.fsf@cs.uga.edu> <87vgd0m5us.fsf@orion.bln.pmsf.de> Mail-Copies-To: never From: Erik Naggum Message-ID: <3222747467111424@naggum.net> Organization: Naggum Software, Oslo, Norway Lines: 149 User-Agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.1 MIME-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit Date: Fri, 15 Feb 2002 07:37:45 GMT X-Complaints-To: abuse@chello.no X-Trace: news01.chello.no 1013758665 212.186.234.171 (Fri, 15 Feb 2002 08:37:45 MET) NNTP-Posting-Date: Fri, 15 Feb 2002 08:37:45 MET Xref: archiver1.google.com comp.lang.lisp:26584 * "Wade Humeniuk" | I think hash-tables are meant to store larger amounts of unordered data and | I cannot see using the :initial-contents to populate a 1000+ key/value pairs | (or for that matter 100,000 values). I would rebel at doing the typing. | For me, storing 2 key/value or even 100 values in a hash table is just not | worth it. When a hash-table is all you have, you tend to optimize it heavily. The same goes for regexps. Both hash-tables and regular expressions are very powerful and general tools, but they turn into monstrosities when all the more specialized tools are effectively removed from the language they use. Common Lisp has the disadvantage compared to the one-trick languages that the different access functions for various kinds of mappings between key and value are precisely that -- different. When there is only one way to do it, being offered several ways is just confusing, and so people who think "hash-table" when they should have thought "key-value mapping" will miss simple and elegant things like property lists or association lists. I mean, if { "foo" => "bar", "baz" => "zot" } is so great, why is not (("foo" . "bar") ("baz" . "zot"))? Is it _only_ the sugar-coated syntax that is so visually appealing to some? If so, a Common Lisp programmer will write a small parser function that is called via the reader-macro support to read this list. Why is this not more used? I belive it is because most people who pine for other syntaxes have no clue how to go about specifying them or writing parsers for them, and because once you have grown that clue, you do not need it or, more importantly, want it. But what the heck. Proof of concept follows. (let ((mapping-types ())) (defun readtable-mapping-type (readtable) (check-type readtable readtable) (or (cdr (assoc readtable mapping-types)) :alist)) (defun (setf readtable-mapping-type) (mode readtable) (check-type readtable readtable) (check-type mode (member :alist :plist :eq :eql :equal :equalp)) (let ((existing (assoc readtable mapping-types))) (if existing (setf (cdr existing) mode) (push (cons readtable mode) mapping-types)))) (defun mapping-reader (stream char) (declare (stream stream) (ignore char)) (loop with mapping-type = (readtable-mapping-type *readtable*) with key and value with hashtable = (case mapping-type (:eq (make-hash-table :test #'eq)) (:eql (make-hash-table :test #'eql)) (:equal (make-hash-table :test #'equal)) (:equalp (make-hash-table :test #'equalp))) do (when (eql #\} (peek-char t stream t nil t)) (read-char stream t nil t) (loop-finish)) (setq key (read stream t nil t)) (if (eql #\= (peek-char t stream t nil t)) (read-char stream t nil t) (error 'parse-error :stream stream)) (unless (eql #\> (read-char stream t nil t)) (error 'parse-error :stream stream)) (setq value (read stream t nil t)) (case (peek-char t stream t nil t) (#\, (read-char stream t nil t)) (#\} t) (t (error 'parse-error :stream stream))) if (eq mapping-type :alist) collect (cons key value) into list else if (eq mapping-type :plist) collect key into list and collect value into list else do (setf (gethash key hashtable) value) finally (case mapping-type ((:alist :plist) (return (list 'quote list))) (t (return hashtable)))))) Now, suppose the above is available in the Common Lisp world. A file that uses this syntax could go like this: (eval-when (:execute :compile-toplevel) (setq *readtable* (copy-readtable)) (set-macro-character #\{ #'mapping-reader) (set-syntax-from-char #\} #\))) (eval-when (:execute :compile-toplevel) (setf (readtable-mapping-type *readtable*) :alist)) (defparameter *roman-alist* { "I" => 1, "V" => 5, "X" => 10, "L" => 50, "C" => 100, "D" => 500, "M" => 1000 }) (eval-when (:execute :compile-toplevel) (setf (readtable-mapping-type *readtable*) :plist)) (defparameter *roman-plist* { "I" => 1, "V" => 5, "X" => 10, "L" => 50, "C" => 100, "D" => 500, "M" => 1000 }) (eval-when (:execute :compile-toplevel) (setf (readtable-mapping-type *readtable*) :equal)) (defparameter *roman-hash* { "I" => 1, "V" => 5, "X" => 10, "L" => 50, "C" => 100, "D" => 500, "M" => 1000 }) I chose this design because I figured that it would be annoying to change the data if the access functions would need to change and that it would be more convenient to specify this via a readtable-local setting than add a lot more syntax for it. If you have support for the in-syntaax form and named readtables, creating a named readtable to invoke this syntax would obviously be the best solution. Now, if you want other kinds of things to go on within { }, it is not a lot of trouble to build a parser by hand, provided you are not a moron and create syntaxes the like of C++ or some other horrible monstrosity. Creating a writer for alists, plists, and hashtables that write data in this syntax is left as an exercise for the reader. Copyright notice: Copyright © 2002 Erik Naggum. The code may be used to create commercial products without charge or other license provided that the author is credited in each source file that uses it or the syntax it allows. Creation of derivative works is permitted under two conditions: No rewrites to use if* or not to use loop are allowed, and the author must be notified by e-mail at . Search engines are permitted to index and retrieve this article in perpetuity and present to users in either normal text or html-ized form, but no other form of re- publication is permitted. Inclusion of this material in some "cookbook" is expressly not permitted. /// -- In a fight against something, the fight has value, victory has none. In a fight for something, the fight is a loss, victory merely relief.