This is a naive, persisted, in memory (lazy loading) data store for Common Lisp.
A getf (and dig) that works with "Generalized Variables". Its simple and should not change much but no gaurentees. If you consider using it in your project ping me in the issues on gitlab so I know that I am affecting some one when I trash the api ;)
To undestand what all of this is about have a look at this setf documentation
Generalized Variables see https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node80.html
Here is the first 3 paragraphs from that dock:
"In Lisp, a variable can remember one piece of data, that is, one Lisp object. The main operations on a variable are to recover that object and to alter the variable to remember a new object these operations are often called access and update operations. The concept of variables named by symbols can be generalized to any storage location that can remember one piece of data, no matter how that location is named. Examples of such storage locations are the car and cdr of a cons, elements of an array, and components of a structure."
"For each kind of generalized variable, typically there are two functions that implement the conceptual access and update operations. For a variable, merely mentioning the name of the variable accesses it, while the setq special form can be used to update it. The function car accesses the car of a cons, and the function rplaca updates it. The function symbol-value accesses the dynamic value of a variable named by a given symbol, and the function set updates it."
"Rather than thinking about two distinct functions that respectively access and update a storage location somehow deduced from their arguments, we can instead simply think of a call to the access function with given arguments as a name for the storage location. Thus, just as x may be considered a name for a storage location (a variable), so (car x) is a name for the car of some cons (which is in turn named by x). Now, rather than having to remember two functions for each kind of generalized variable (having to remember, for example, that rplaca corresponds to car), we adopt a uniform syntax for updating storage locations named in this way, using the setf macro."
My opinion is that "they" could have done a getf for places as well. Enters getx (pronounced get x) rather than having to remember the names and syntax order of such access functions for each kind of generalized variable (having to remember, for example, that getf = (place accessor) and gethash = (accessor place), I adopt a uniform syntax for updating and fetching such storage locations, using the getx generic method.
It can be argued that most editors show you the order as you type anyway, well what happens when you have a lot of lines of code and you used one type of place lets say a hashtable and now you want to change it to plists? You are not just going to do a quick text replace :).
Generatiing such access code (for whatever reason) using getx should be simpler as well.
But WAIT there is more!!! You get a digx as well!!! :P
Not a NEW IDEA:
This is not a NEW IDEA, and examples of such a getf can be found out there. For example you can find some code in lisp21.
Curiously a getx function was one of the first utility functions I tried to write for my self when I started using lisp. Maybe it was because I was working on a simple object database, I was trying to see of objects or lists where better for my design and went to and fro a couple of times. It obviously left me scared ;)
> (getx (list :eish 1) :eish) 1 > (let ((plist (list :eish 1))) (setf (getx plist :eish) 2) (getx plist :eish)) 2 > (getx (list 'eish 1) 'eish) 1 > (let ((plist (list 'eish 1))) (setf (getx plist 'eish) 2) (getx plist 'eish)) 2 > (getx (list 'eish 1) #'(lambda (place) (getf place 'eish))) 1 > (let ((plist (list 'eish 1))) (setf (getx plist #'(lambda (place value) (setf (getf place 'eish) value))) 2) (getx plist #'(lambda (place) (getf place 'eish)))) 2 (defclass thingy () ((eish :initarg :eish :accessor eish))) (setf x (make-instance 'thingy 'eish 1)) > (getx x 'eish) 1 > (setf (getx x 'some-slot) 2) 2 > (getx x 'some-slot) 2
Doing a (setf getf) on a property that does not exist does nothing but for getx it adds the property with the new value.
Default (setf getf) behaviour:
(defun add-stuff (container) (setf (getf container :new) 234)) > (let (x) (add-stuff x) x) NIL > (let ((x (list :a 1))) (add-stuff x) x) (:A 1)
(setf getx) behaviour for plist:
(defun add-stuff (container) (setf (getx container :new) 234)) ;;Could not fix x=nil >(let (x) (add-stuff x) x) NIL ;;But could do something about adding properties to an a list > (let ((x (list :a 1))) (add-stuff x) x) (:A 1 :NEW 234)
> (digx (list :eish (list :huh 1)) :eish :huh) 1 > (let ((x (list :eish (list :huh 1)))) (setf (cl-getx:digx x :eish :huh) 'huh) x) (:EISH (:HUH HUH)) > (let ((plist (list :eish (list 'huh (list :erm 1))))) (setf (cl-getx:digx plist :eish 'huh #'(lambda (place &optional value) (setf (getf place ':erm) value))) 'huh) plist) (:EISH (HUH (:ERM HUH)))
Tests can be found in tests.lisp just load it after you quickload cl-getx and check your repl for the output.
Lots more tests are needed, especially setf ones, but the current set proves that the basics work.
Tests pass on sbcl and ecl.