A blog about skating and cycling, or vice versa

Mostly ORMless#

Mon, 29 Oct 2007 16:15:33 +0000

Some years ago I wrote about Object-Relational Mapping. Slightly more recently I actually read something about relational theory - which has tended to reinforce my opinions rather than anything else, and which led to the development of Septeql

(Ten second demonstration that mapping objects to rows is in principle wrong: a table row with attributes NAME="Cthulhu", CITY="R'lyeh", DEPARTMENT="Human Resources" is not actually the representation of Cthulhu, just an assertion that such a being exists (somewhere). Thus, two identical rows does not correspond to two lots of tentacles, merely two such assertions. Now where's your object identity?)

So, I don't know whether I'm doing this for the appeal to elegance or for the opimisation challenge of removing about a zillion (lit: 27) pointer-chasing database roundtrips induced by the ORM approach but either is justifiable in my mind: for each page, let's do a sodding great join on everything we could possibly want (about 1k of characters after transformation to SQL) and do all subsequent processing in memory.

In Lisp we're choosing to represent a relation (result set) as a list of plists (IATWBPF) and ideally we need some tools to process them without further database access.

(defmacro k& ((&rest attributes) &body forms)
  (let* ((args (gensym "ARGS"))
	 (macrolets (loop for n in attributes 
		       collect `(,n (ref ,args ,(intern (symbol-name n)
						    :keyword))))))
    `(lambda (,args) (symbol-macrolet (,macrolets) ,forms))))

;; thus #| (let ((rows (list (list :a 10 :b 19 :c 21) (list :a 1 :b 3 :c 5)))) (list (find-if (k& (b) (= b 19)) rows) ; select (mapcar (k& (a b) (list :a a :b b)) rows) ; project (reduce #'+ rows :key (k& (c) c)) ; summarise (progn (map nil (k& (b) (setf b 'foo)) rows) rows) ; in-place update )) |#

I'm rewriting the Stargreen shopping cart page to use this, and it seems to be working quite well - at least, most of the problems I'm running up against aren't anything to do with databases but are back-button issues, or UI or whatever. The only DBMS sticking point is still that Lisp doesn't have a good concept to map SQL NULL onto, but if we design to avoid that, this does mostly seem to satisfy the "I can use this without continual recourse to documentation/source code" criterion. which I used to decide that Septeql succeeds where Sexql didn't.