Currently supported implementations are Allegro CL, SBCL, LispWorks, CLISP, and OpenMCL. It suffers from what appears to be a bug in CMUCL's CLOS implementation, so is only partially supported there. In the past it has worked on Corman Lisp and MCL, and resurrecting this support is most likely trivial.
Find the current version and access to the mailing-list on common-lisp.net, the source under cvs here
After trying it out a bit, my understanding is this: You can define objects whose slots trigger events when their values are modified. These slots are called "cells."
I think that's it; the rest is detail.
Two kinds of things happen when a cell's value is changed:
cell-1's value depends on cell-2. If cell-2 is changed, cell-1 will be updated.
(Hmm, does this scale? You can make 100 objects, but each of their cells {cell-1..cell-N} will have to share observers named {cell-1..cell-N}.)
Basic example:
(defmodel my-cell ()
((cell-1 :cell t
:initform (c-in 1) ; c-in allows you to to modify this cell, with (for example) setf
:accessor cell-1)
(cell-2 :cell t
:initform (c? (* (^cell-1) 2)) ; c? bars you from modifying this cell, signalling an error; it only changes when cell-1 does
:accessor cell-2)))
(defparameter *cell* (make-be 'my-cell))
Cell-2 depends on cell-1's value. If cell-1 were to change, cell-2's value would change to be twice cell-1. So, let's try it out:
CL-USER> (cell-1 *cell*) 1 CL-USER> (cell-2 *cell*) 2 CL-USER> (setf (cell-1 *cell*) 10) 10 CL-USER> (cell-1 *cell*) 10 CL-USER> (cell-2 *cell*) 20
We can see that the syntax diverges a little from normal CLOS:
defclass -> defmodel (Reasoning -- syntactic sugar will be bound which normal CLOS can't accomodate.)
make-instance -> make-be
:cell slot option.
(c-in ...) and (c? ...) forms.
[todo: discuss observers]
(defmodel my-model () ((cell-1 :cell t ...) (cell-2 :cell :ephemeral ...)))
cell-1 will keep its new value when changed.
cell-2, however, will revert instantly to whatever it was initialized with, after dependencies/observers on cell-2 are triggered.
(Philosophical background: From my (nonexpert) understanding, discrete processes are categorized into states and events, corresponding to :cell t and :cell :ephemeral, respectively. This is taken from Sowa's knowledge representation book.)
(The docs mention :delta, but seems unsupported.)
Cell-1 depends on cell-2 if (and only if) cell-1 looked at cell-2 the last time cell-1's c? code ran. Kenny Tilton explained on usenet:
So dependencies will vary after every invocation of, say:(c? (if (^a)(^b)(^c)))between A and B or A and C.
Interestingly, this means inelegant code can create problems:
(c? (let ((b (^b))(c (^c))) (if (^a) b c)))...always produces dependencies A, B, and C, which is a lie.
Note, btw, that dependencies are dynamic, not lexical: call a function that accesses a cell and you still get a dependency.
From Kenny Tilton's post:
(^slotname) - within a c? or def-c-output form, holds the current value of that slot
self - (look into this. only know it's bound by default within a def-c-output form to the current instance)
.cache - within a c? form, holds the current value of a cell
.parent - [todo, dunno what it does]
.cause - symbol-macro of some sort, I don't suggest you use this as it seems unsupported
Call (cell-reset) if c-stop is invoked. c-stop apparently halts the Cells system when circular dependencies are detected, and maybe other scenarios too.
An error message like the following may occur (formatted to be more readable):
0> c-calculate-and-set breaking on circularity | [?#:=[236]LOCATION/# ] C-STOP> stopping because (cell ~a midst askers: ~a [?#: =[236]LOCATION/# ] ([?#: =[236]RESPONSE/# ] [?#: =[236]LOCATION/# ])) c-break > stopping > (cell ~a midst askers: ~a [?#: =[236]LOCATION/# ] ([?#: =[236]RESPONSE/# ] [?#: =[236]LOCATION/# ]))
So we execute:
CL-USER> (cell-reset) NIL
This page is linked from: computed-class KR
CLiki pages can be edited by anyone at any time. Imagine a fearsomely comprehensive disclaimer of liability. Now fear, comprehensively