Common Lisp library that allows to make variables and slots that change behavior depending on their values (so, using special values you can e.g make lazy values)
[Please notice that I'm currently kind of a Common Lisp newb, so the code probably isn't exactly good...]
(defvar-ext *a* (let ((x 0)) (lazy (incf x))))
*a* ;;=> 1
*a* ;;=> 2
;...
When you use a variable defined using one of defvar-ext
, defparameter-ext
, let-ext
or let*-ext
, those will actually be symbol-macrolets (Notice: They will not be boundp
) and the get-value generic function will be called on the value (this defaults to identity) and set-value will be called when setting them (for the arguments of set-value see values.lisp). Thus, you can have something like variables that can be lazily evaluated or like above counters.
values can also be used to trace variables, like this:
(defvar-ext *b* (traced 2 ; initial value
(format T "*b*: ~A ~:[to ~A~;~]" ;on set/get
traced-value-op
(eq traced-value-op :get)
traced-value-new)))
*b*
;;-> *b*: GET
;;=> 2
(setf *b* 3)
;;-> *b*: SET to 3
;;=> 3
*b*
;;-> *b*: GET
;;=> 3
If you want to get at the real value of the variable, i.e. the value object, you can use value-quote
like this:
(defvar-ext *b* (lazy 2))
*b* ;;=> 2
(value-quote *b*) ;;=> #<VALUES::LAZY-VALUE ...>
All *-ext
forms implicitly value-quote
their arguments (the initial values). This includes setf-ext
which is meant for this purpose (You can use setf
instead if you don't need value-quote
'd values). To undo value-quote
you can simply use value-unquote
.
There is a class called metaclass-ext-mixin
. Using a metaclass that inherits from this results in slots that have the effects described above for variables defined using *-ext
forms.
There is a standard-class-ext
(which is simply a metaclass inheriting from standard-class
and metaclass-ext-mixin
and defining a validate-superclass
method), so you can use this:
(defclass foo ()
((bar :accessor foo-bar :initarg :bar))
(:metaclass standard-class-ext))
(defvar *foo* (make-instance 'foo
:bar (let ((x 0))
(lazy (incf x)))))
(foo-bar *foo*) ;;=> 1
(foo-bar *foo*) ;;=> 2
;...
This depends on the Metaobject Protocol. To make this possible in a portable way, values uses the package closer-mop (though, it shouldn't be to hard to port to e.g. the sbcl implementation of the MOP)
If you want your own class to affect the behavior of the variable defined using a *-ext
form, you can specialize on the following methods:
get-value value
is called when the value of the variable is read. value is the current value of the variable (i.e. an instance of your own class).
set-value value setter-function new-values
is called when the value of the variable is written (using setf
or setf-ext
). It takes these arguments:
value
- this is the current value (before setting) of the variable (i.e. an instance of your own class)setter-function
- this is a function that, if called, sets the variable to the new value. It takes as many optional arguments as there are elements innew-values
that default to the values innew-values
(and can be used to change the value that is to be set)new-values
- This is a list of the new values of the variable. (This will usually be only one. This is the list of the values of the variables returned as the third return value ofget-setf-expansion
)
Further explanation can be found in values.lisp
(as comments) and may be added here someday...