Update of /project/rjain-utils/cvsroot/formulate/src In directory cl-net:/tmp/cvs-serv29200/src
Modified Files: formulate.lisp metaobjects.lisp package.lisp variables.lisp Removed Files: tests.lisp Log Message: Refactor a bit and get everything working for the basic lazy evaluation and unconditional propagation scenario.
--- /project/rjain-utils/cvsroot/formulate/src/formulate.lisp 2007/11/02 20:45:39 1.1.1.1 +++ /project/rjain-utils/cvsroot/formulate/src/formulate.lisp 2009/11/04 21:41:35 1.2 @@ -1,48 +1,108 @@ (in-package :formulate)
+(deftype list-of (elt-type) + 'list) + (defvar *formulating* '() - "Dynamically rebound each time we start computing a formula with the -FORMULATOR CONSed to the front of it.") + "The FORMULATOR, if any, that is being evaluated.")
(define-condition set-formulated-location (cell-error) ())
-(defclass standard-formulator-source () - (reverse-dependencies :initform '() :type list :accessor reverse-dependencies) - (value) - (eager-propagation :initform t)) +(defgeneric formulator-value (formulator + &optional unbound-condition cell-name))
-(defclass standard-formulator-sink () - ((eager-recomputation :initform nil)))
-(defclass standard-formulator (standard-formulator-source standard-formulator-sink) - ((formula :initarg formula :initform (error "need to specify a formula") - :accessor formulator-formula) - (formula-function :initarg formula-function :initform (error "need to specify a formula-function") :type function - :accessor formulator-formula-function))) +(defmethod formulator-value :around (formulator + &optional unbound-condition cell-name) + (when *formulating* + (note-formula-dependency formulator *formulating*)) + (if (formulator-value-validp formulator) + (call-next-method) + (error unbound-condition :name cell-name))) + +(defgeneric formulator-value-changed (sink source new-value old-value)) + +(defclass simple-formulator-source () + ((dependents :initform '() + :type (list-of formulator-sink) + :accessor formulator-dependents) + (value)) + (:documentation "FORMULATOR-SOURCE implementation that unconditionally + notifies all sinks that depend on it every time its value is changed.")) + +(defmethod initialize-instance :after ((formulator simple-formulator-source) + &key ((formula formula)) ((formula-function formula-function))) + (when formula-function + (setf (slot-value formulator 'value) (funcall formula-function)))) + +(defmethod formulator-value-validp ((source simple-formulator-source)) + (slot-boundp source 'value)) + +(defmethod formulator-invalidate ((source simple-formulator-source)) + (slot-makunbound source 'value)) + +(defmethod formulator-value ((formulator simple-formulator-source) + &optional cond cell) + (slot-value formulator 'value)) + +(defmethod (setf formulator-value) (new-value (formulator simple-formulator-source)) + (let ((old-value (and (formulator-value-validp formulator) + (formulator-value formulator))) + (result (setf (slot-value formulator 'value) new-value))) + (dolist (dependent (formulator-dependents formulator)) + (formulator-source-value-changed dependent formulator new-value old-value)) + result))
-(defun formulate (formulator unbound-condition cell-name) - (if (null formulator) - (error unbound-condition :name cell-name) - (if (slot-boundp formulator 'value) - (slot-value formulator 'value) - (compute-formula formulator)))) - -(defmethod compute-formula ((formulator formulator)) - (note-formula-dependency formulator) - (setf (reverse-dependencies formulator) '()) - (let ((*formulating* (cons formulator *formulating*))) - (setf (slot-value formulator 'value) - (funcall (formulator-formula-function formulator)))) - (when (slot-value formulator 'eager-propagation) - (mapcar (lambda (dependent) (note-dependency-value-changed dependent formulator)) - (slot-value formulator 'reverse-dependencies)))) - -(defmethod note-formula-dependency ((formulator standard-formulator)) - (dolist (surrounding-formulator *formulating*) - (pushnew formulator (reverse-dependencies surrounding-formulator)))) - -(defmethod note-dependency-value-changed ((dependent standard-formulator) (dependency standard-formulator)) - (slot-makunbound dependent 'value) - (when (slot-value dependent 'eager-recomputation) - (compute-formula dependent))) \ No newline at end of file +(defclass formula-formulator-sink () + ((formula :initarg formula + :accessor formulator-formula) + (formula-function :initarg formula-function + :initform (error "need to specify a formula-function") + :type function + :accessor formulator-formula-function)) + (:documentation "FORMULATOR-SINK implementation that recomputes the + formula every time it is asked for a value.")) + +(defmethod formulator-value ((formulator formula-formulator-sink) + &optional cond cell) + (funcall (formulator-formula-function formulator))) + +(defmethod formulator-value-validp ((formulator formula-formulator-sink)) + (slot-boundp formulator 'formula)) + +(defclass lazy-formula-formulator-sink (formula-formulator-sink) + ((source :initarg source + :initform (make-instance 'simple-formulator-source) + :accessor formulator-source + :documentation "FORMULATOR-SOURCE that contains the cached + value and propagates changes to sinks that refer to this + formulator's parent cell.")) + (:documentation "FORMULATOR-SINK implementation that lazily recomputes + and caches the formula's value.")) + +(defmethod formulator-dependents ((formulator lazy-formula-formulator-sink)) + (formulator-dependents (formulator-source formulator))) + +(defmethod (setf formulator-dependents) (new-value (formulator lazy-formula-formulator-sink)) + (setf (formulator-dependents (formulator-source formulator)) new-value)) + +(defmethod formulator-value ((formulator lazy-formula-formulator-sink) + &optional cond cell) + (let ((source (formulator-source formulator))) + (if (formulator-value-validpxo source) + (let ((*formulating* nil)) + (formulator-value source cond cell)) + (let ((*formulating* formulator)) + ;; TODO: remove dependencies when dependencies change + (setf (formulator-value source) (call-next-method)))))) + +(defmethod formulator-invalidate ((formulator lazy-formula-formulator-sink)) + (formulator-invalidate (formulator-source formulator))) + +(defmethod note-formula-dependency (source sink) + (pushnew sink (formulator-dependents source))) + +(defmethod formulator-source-value-changed + ((sink lazy-formula-formulator-sink) source new-value old-value) + (formulator-invalidate sink)) --- /project/rjain-utils/cvsroot/formulate/src/metaobjects.lisp 2007/11/02 20:45:39 1.1.1.1 +++ /project/rjain-utils/cvsroot/formulate/src/metaobjects.lisp 2009/11/04 21:41:35 1.2 @@ -7,62 +7,75 @@ t)
(defclass formulated-slot-definition (standard-slot-definition) - ((formulator-class :initform 'standard-formulator :initarg formulator-class :accessor formulator-class) - (formulator-options :initform '() :initarg formulator-options :accessor formulator-options))) + ((formulator-class :initarg formulator-class + :accessor formulator-class) + (formulator-options :initform '() + :initarg formulator-options + :accessor formulator-options)))
(defclass formulated-direct-slot-definition (formulated-slot-definition standard-direct-slot-definition) ())
+(defmethod initialize-instance :after ((instance formulated-direct-slot-definition) + &key ((formula-p formula-p))) + "default formulator-class based on whether this is a formula or not" + (unless (slot-boundp instance 'formulator-class) + (setf (slot-value instance 'formulator-class) + (if formula-p + 'lazy-formula-formulator-sink + 'simple-formulator-source)))) + (defmethod slot-definition-initfunction ((slotd formulated-direct-slot-definition)) (lambda () (apply 'make-instance (formulator-class slotd) 'formula (slot-definition-initform slotd) 'formula-function (call-next-method) (formulator-options slotd))))
-(defmethod initialize-instance :after ((instance formulated-slot-definition) &key ((formula-p formula-p) t)) - (declare (ignore formula-p)) - ;; FORMULA-P is already reflected in the class chosen by DIRECT-SLOT-DEFINITION-CLASS - ) - -(defmethod direct-slot-definition-class ((class formulated-class) - &key ((formula-p formula-p) nil) &allow-other-keys) - (if formula-p - 'formulated-direct-slot-definition - 'formulated-source-)) +(defmethod direct-slot-definition-class ((class formulated-class) &key &allow-other-keys) + ;; formula-p only indicates whether this is a formula sink as well as + ;; a source. + 'formulated-direct-slot-definition)
(defclass formulated-effective-slot-definition (formulated-slot-definition standard-effective-slot-definition) ())
-(defvar *computing-formulated-eslotd* nil) - (defmethod effective-slot-definition-class ((class formulated-class) &key &allow-other-keys) - (if *computing-formulated-eslotd* - 'formulated-effective-slot-definition - (call-next-method))) + ;; formula-p only indicates whether this is a formula sink as well as + ;; a source. + 'formulated-effective-slot-definition)
(defmethod compute-effective-slot-definition ((class formulated-class) slot-name dslotds) - (declare (type list dslotds)) - (let ((*computing-formulated-eslotd* - (find-if (lambda (slotd) (typep slotd 'formulated-direct-slot-definition)) dslotds))) - (call-next-method))) + (let ((eslotd (call-next-method)) + (most-specific-fdslotd + (find-if + (lambda (slotd) + (typep slotd 'formulated-direct-slot-definition)) + dslotds))) + (setf (slot-value eslotd 'formulator-class) + (formulator-class most-specific-fdslotd)) + eslotd))
(defvar *me*)
-(defmethod slot-value-using-class ((class formulated-class) object (slotd formulated-effective-slot-definition)) - (let ((*me* object)) - (formulate (call-next-method) 'unbound-slot (slot-definition-name slotd)))) +(defvar *get-slot-formulator* nil)
-(define-condition set-formulated-slot (set-formulated-location) - ()) - -(defmethod (setf slot-value-using-class) (new-value - (class formulated-class) object (slotd formulated-effective-slot-definition)) - (declare (ignore new-value class)) - (call-next-method) - #+nil ; this doesn't seem to work... - (if (typep object 'formulator) +(defmethod slot-value-using-class :around + (class object (slotd formulated-effective-slot-definition)) + (if *get-slot-formulator* (call-next-method) - (error 'set-formulated-slot :name (slot-definition-name slotd)))) + (let ((*me* object)) + (formulator-value (call-next-method) 'unbound-slot (slot-definition-name slotd))))) + +(defmethod slot-formulator-using-class (class object (slotd formulated-effective-slot-definition)) + (let ((*get-slot-formulator* t)) + (slot-value-using-class class object slotd))) + +(defmethod (setf slot-value-using-class) :around + (new-value + class object (slotd formulated-effective-slot-definition)) + (if (slot-boundp-using-class class object slotd) + (setf (formulator-value (slot-formulator-using-class class object slotd)) new-value) + (call-next-method)))
(declaim (inline my)) (defun my (slot) --- /project/rjain-utils/cvsroot/formulate/src/package.lisp 2007/11/02 20:45:35 1.1.1.1 +++ /project/rjain-utils/cvsroot/formulate/src/package.lisp 2009/11/04 21:41:35 1.2 @@ -1,11 +1,15 @@ (defpackage :formulate (:export #:formulator - #:standard-formulator + #:simple-formulator-source + #:formula-formulator-sink + #:lazy-formula-formulator-sink #:formulated-class #:my #:formula-p + #:formulator-class + #:formulator-options #:define-formulated-variable) - (:use :cl :mop)) + (:use :cl #.(first '(#+sbcl :sb-mop :mop))))
(defpackage :formulate-user (:use :cl :formulate)) \ No newline at end of file --- /project/rjain-utils/cvsroot/formulate/src/variables.lisp 2007/11/02 20:45:39 1.1.1.1 +++ /project/rjain-utils/cvsroot/formulate/src/variables.lisp 2009/11/04 21:41:35 1.2 @@ -1,25 +1,22 @@ (in-package :formulate)
(defmacro define-formulated-variable (name formula - &key declare + &key declare documentation - (formulator-class 'standard-formulator) + (formulator-class 'lazy-formula-formulator-sink) formulator-options) `(progn (define-symbol-macro ,name (formulate-variable ',name)) (setf (documentation ',name 'variable) ,documentation) - (setf (get name 'formulator) + (setf (symbol-value ',name) (make-instance ',formulator-class 'formula ',formula 'formula-function (lambda () (declare ,@declare) ,formula) - ,@formulator-options)))) + ,@formulator-options)) + ',name))
(defun formulate-variable (name) - (formulate (get name 'formulator) 'unbound-variable name)) - -(define-condition set-formuated-variable (set-formulated-location) - ()) + (formulator-value (symbol-value name) 'unbound-variable name))
(defun (setf formulate-variable) (new-value name) - (declare (ignore new-value)) - (error 'set-formulated-variable :name name)) + (setf (formulator-value (symbol-value name)) new-value))