Access update – corrected SETF expanders and more examples

In the first released version of access I defined the setf versions as (defun (setf accesses) (new o &rest keys)…). In order to make this work out for plists and alists (where adding a key can result in a new HEAD element), I was forced to return the updated object rather than the “new” value that setf usually returns. I was unhappy with this oddity at the time but didn’t know directly how to fix it (obviously some macrology was in order to capture the “place” being modified).

Today I looked into the docs for define-set-expander and saw how to transform my code into “correct” setf’s. To do this i transformed my previous setf functions into set-access and set-accesses which return (values new-value possibly-new-object). I then define my setf expanders in terms of calling those functions and setting the place passed in to possibly-new-object. It took a little while to figure out and I’m still not entirely sure I wrote the optimal common lisp for this. However I was able to elide the outer setf from these expressions in the tests (setf pl (setf (access pl ‘one) ‘new-val)) and now the new (setf (access pl ‘one) ‘new-val) returns ‘new-val as would be expected.

There were some requests for more, better examples of where access might be useful:

  • My html components have a plist representing direct html attributes. I update these with (setf (accesses ctl ‘attributes ‘name) “myFormName”) and its correlary (accesses ctl #’attributes :name). Note that both forms work even though one uses a local symbol and one a keyword (they are compared by symbol-name so that I can think about it less). Also I am ok referring to the attributes function by name or function object (both will result in calling the attributes function on ctl).
  • Another example from the web domain: I often store a reference to a database object on the control that is responsible for displaying it. Thus getting the database primary key off of the data for a control can be (accesses client-form ‘data ‘adwolf-db:accountid). This allows me (where useful) to ignore the difference between an unsaved, new object and an object that hasn’t been created yet (for things like putting the id in the url, the difference is irrelevant).
  • While not currently implemented this way, my group-by library which groups items into nested alists or hashtable could potentially use access to handle the different implementations
  • Printing my database objects in debug / log messages, I want to output some columns (but only if the database object has those). This way I can define one printer for all my db objects with a minimum of fuss
    (defmethod print-object ((o clsql:mssql-db-object) (s stream))
    "Print the database object, and a couple of the most common identity slots."
    (print-unreadable-object (o s :type t :identity t)
    (iter (for c in '(id accountid serviceid transactionid title amount name))
    (for v = (access o c))
    (when v (format s "~A:~A " c v)))
    ))

In general I find access useful whenever I need to operate on some set of keys that may or may not exist in a dictionary-like object and I don’t care to receive any errors related to missing keys.

TODO:

  • A keys / values interface to ease arbitrary dictionary iteration would be a worthy addition (alexandria seems to have all the relevant functions implemented, so it would mostly be a dispatch to those)
  • When a dictionary doesnt exist, there should be someway of telling it how to create that dictionary (currently you will get a plist).
  • Extensibility to allow support for other dict like structures.

New Common Lisp project: Access

Access is a common lisp library I just culled out of our immense utility mud ball and refactored into a library all its own. Access makes getting and setting values in common data structures support a single unified api. As such you could access a specific key from an alist stored in a hashtable stored in the slot of an object as (accesses o ‘k1 ‘k2 ‘k3). It also supports setting values (setf (accesses o ‘k1 ‘k2 ‘k3) “new-val”). Obviously there are some limitations to this approach, but for me, with my coding conventions, I don’t tend to run into them (see the README for details).

Access has removed some of my need for forms like (awhen a (awhen (fn1 it) (fn2 it))) with (access a ‘fn1 ‘fn2). To me, it allows me to more accurately express what I am trying to do while ignoring the vagaries of shifting implementation details. It also eases setting values in nested objects because it handles propagating the value up the chain rather than me having to do that myself (ie adding a new key-value pair to a the front of an alist stored in an object, automatically saves the new resulting alist in the object). I don’t expect that this is tasteful coding, but it is easier and allows me to not get mired down trying to decide if I want it to be an alist, plist, hashtable, or object because the cost to change it later is essentially zero.

Performance is rarely in issue in the apps that I tend to write. However, if it were, I would not use access as it does significant type and dispatch analysis that could be avoided by using the specific access functions of the data structure I am using.

A dot syntax familiar to those who use javascript/python/ruby type languages is available as well. This transforms calls like foo.bar.bast into (accesses foo ‘bar ‘bast). I don’t use this syntax as I tend to prefer the lisp function-call syntax, but it seems to be an oft requested / discussed feature, and I had fun writing the code.